using System;
using System.Text;
using System.Security.Cryptography;
using System.Collections;
using System.IO;
using System.Data.SqlClient;
using System.Data;
using System.Runtime.InteropServices;  
using System.Threading;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using Av.Utils;
//using SnorasEcr;

namespace Solo.Core
{
    #region Encodings
    public static class PreloadedEncodings
    {
        private static Dictionary<string, System.Text.Encoding> _dict = new Dictionary<string, Encoding>();
        public static int Preload(params string[] encodings)
        {
            int count = 0;
            foreach (string enc in encodings)
            {
                try
                {
                    _dict[enc] = System.Text.Encoding.GetEncoding(enc);
                    count++;
                }
                catch
                {
//                    Phoenix.TraceLog("Failed to load encoding {0}", enc);
                }
            }
            return count;
        }

        public static Encoding Get(string name)
        {
            return _dict[name];
        }
    }
    #endregion

    #region DataPair
    public class DataPair
    {
        private string m_Key = null;
        private string m_Value = null;
        public DataPair(string key, string value)
        {
            m_Key = key;
            m_Value = value;
        }

        public string Key { get { return m_Key; } }
        public string Value { get { return m_Value; } }

        public override string ToString()
        {
            return m_Key == null ? "(null)" : m_Key;
        }
    }
    #endregion

    #region TimeOut

    /// <summary>
    /// Time out exception
    /// </summary>
    public sealed class TimeoutException : Exception
    {
        /// <summary>
        /// Time out exception
        /// </summary>
        public TimeoutException() : base ("Time Out")
        {
        }
    }
    /// <summary>
    /// 
    /// </summary>
    public class TimeOut
    {
        public readonly bool Fatal = true;
        //private static double DIF = TimeSpan.TicksPerMillisecond / 1000;
        private Int64 to;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeout">Object timeout in milli seconds</param>
        public TimeOut(Int32 timeout)
        {
            to = DateTime.Now.Ticks + (timeout * TimeSpan.TicksPerMillisecond);
        }

        public TimeOut(Int32 timeout, bool fatal) : this(timeout)
        {
            Fatal = fatal;
        }
        /// <summary>
        /// 
        /// </summary>
        internal Int64 Tics
        {
            get 
            {
                Int64 tic = DateTime.Now.Ticks;
                if (to < tic) throw new TimeoutException();
                return (Int32)(to - tic);			
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal Int32 Milliseconds
        {
            get 
            {
                Int64 tic = DateTime.Now.Ticks;
                if (to < tic) throw new TimeoutException();
                return (Int32)((to - tic) / DIFmill);
            }
        }
        private static double	DIFmicro = TimeSpan.TicksPerMillisecond / 1000;
        private static Int64	DIFmill = TimeSpan.TicksPerMillisecond;
        /// <summary>
        /// 
        /// </summary>
        internal Int32 Microseconds
        {
            get 
            {
                Int64 tic = DateTime.Now.Ticks;
                if (to < tic) throw new TimeoutException();
                return (Int32)((to - tic) / DIFmicro);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static implicit operator bool(TimeOut o)
        {
            return DateTime.Now.Ticks > o.to;
        }
    }

    #endregion

    #region RC4
    /// <summary>
    /// Provides RC4 encryption algorithm
    /// </summary>
    public sealed class RC4
    {
        private static void transform(byte[] src, int offset, int count, byte[] dst, byte[] sBox)
        {
            //RC4 encryption process
            //i = (i + 1) MOD 256
            //j = (j + Si) MOD 256
            //Swap Si and Sj
            //t = (Si + Sj) MOD 256
            //K = St
            //K is the XORed with the plaintext to produce the ciphertext, or the ciphertext to produce the plaintext.
            //RSA claims that the algorithm is immune to differential and linear cryptanalysis. The algorithm can also be changed from the 8-bit used above to 16-bit by using a 16 * 16 S-Box, and a 16-bit word. The initial set-up would need to be repeated 65,536 times to keep with the design, but it should be faster.
            int i=0,j=0;
            int il = offset + count;
            for (int ps = offset; ps < il ; ps++ )
            {
                i = ( i + 1 ) & 0xFF;
                j = ( j + sBox[i] ) & 0xFF; 
                byte temp =  sBox[i];
                sBox[i] = sBox[j];
                sBox[j] = temp;
                dst[ps] = (byte)(src[ps] ^ sBox[(sBox[i]+sBox[j]) & 0xFF]);
            }	

        }
        private static byte[] iBox = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255};
        /// <summary>
        /// Transforms data
        /// </summary>
        /// <param name="key">Ley</param>
        /// <param name="buffer">Data</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        /// <returns></returns>
        public static byte[] transform(byte[] key, byte[] key2, byte[] buffer, int offset, int count)
        {
            if (key2 != null)
            {
                int off = key.Length;
                Array.Resize<byte>(ref key, key.Length + key2.Length);
                key2.CopyTo(key, off);
            }
            //RC4 key generation
            //The S-Box is easily generated, using the following:
            //Fill S1 to S255 linearly (i.e. S0 = 0; S1 = 1 ... S255 = 255)
            //Another 256 byte array is then filled with the key, the key is repeated as necessary to fill the entire array.
            //The index j is then set to 0
            //for (i = 0 to i = 255)
            //j = (j + Si + ki) MOD 256
            //Swap Si and Sj
            //fi
            byte[] sBox = (byte[])iBox.Clone();
            int j = 0;
            int x = 0;
            for (int i = 0; i < 256; i++)
            {
                j = (j + sBox[i] + key[x++]) & 0xFF;
                if (x == key.Length) x = 0;
                byte temp =  sBox[i];
                sBox[i] = sBox[j];
                sBox[j] = temp;
            }
            byte[] rez = new byte[count];
            transform(buffer, offset, count, rez, sBox);
            return rez;
        }
    }
    #endregion

    #region Custom RC4
    public static class RC4C
    {
        public static void Encrypt(byte[] src, byte[] iBox)
        {
            #region Encryption Stage1
            {
                byte[] sBox = (byte[])iBox.Clone();
                int j = 0;
                byte b = 0;

                for (int x = 0; x < src.Length; x++)
                {
                    j = (j + sBox[x & 0xFF] + b) & 0xFF;
                    b = src[x];
                    byte temp = sBox[x & 0xFF]; sBox[x & 0xFF] = sBox[j]; sBox[j] = temp;
                    src[x] ^= sBox[(sBox[x & 0xFF] + sBox[j]) & 0xFF];
                }
            }
            #endregion
            #region Encryption Stage2
            {
                byte[] sBox = (byte[])iBox.Clone();
                int j = 0;
                byte b = 0;

                for (int x = src.Length - 1; x >= 0; x--)
                {
                    j = (j + sBox[x & 0xFF] + b) & 0xFF;
                    b = src[x];
                    byte temp = sBox[x & 0xFF]; sBox[x & 0xFF] = sBox[j]; sBox[j] = temp;
                    src[x] ^= sBox[(sBox[x & 0xFF] + sBox[j]) & 0xFF];
                }
            }
            #endregion
        }

        public static void Decrypt(byte[] src, byte[] iBox)
        {
            #region Decryption Stage1
            {
                byte[] sBox = (byte[])iBox.Clone();
                int j = 0;
                byte b = 0;

                for (int x = src.Length - 1; x >= 0; x--)
                {
                    j = (j + sBox[x & 0xFF] + b) & 0xFF;
                    byte temp = sBox[x & 0xFF]; sBox[x & 0xFF] = sBox[j]; sBox[j] = temp;
                    b = src[x] ^= sBox[(sBox[x & 0xFF] + sBox[j]) & 0xFF];
                }
            }
            #endregion
            #region Decryption Stage2
            {
                byte[] sBox = (byte[])iBox.Clone();
                int j = 0;
                byte b = 0;

                for (int x = 0; x < src.Length; x++)
                {
                    j = (j + sBox[x & 0xFF] + b) & 0xFF;
                    byte temp = sBox[x & 0xFF]; sBox[x & 0xFF] = sBox[j]; sBox[j] = temp;
                    b = src[x] ^= sBox[(sBox[x & 0xFF] + sBox[j]) & 0xFF];
                }
            }
            #endregion
        }
    }
    #endregion

    #region RC4CBC

    public sealed class RC4CBC
    {
        public RC4CBC Clone()
        {
            RC4CBC rc4 = new RC4CBC(sBox);
            rc4.i = this.i;
            rc4.j = this.j;
            return rc4;
        }
        public static void Encrypt(byte[] buffer, int offset, int count, byte[] KEY)
        {
            RC4CBC.CreateFromKey(KEY).Encrypt(buffer, offset, count);
        }
        internal byte[] sBox;
        internal int i = 0, j = 0;
        internal RC4CBC(byte[] sBox)
        {
            this.sBox = (byte[])sBox.Clone();
        }

        public byte Encrypt(byte b)
        {
            i = (i + 1) & 0xFF;
            j = (j + sBox[i] + 1) & 0xFF;
            byte sBoxi = sBox[i];
            byte sBoxj = sBox[j];
            sBox[i] = sBoxj;
            sBox[j] = sBoxi;
            j = (j + b) & 0xFF;
            return b ^= sBox[(sBoxi + sBoxj) & 0xFF];
        }
        public byte Decrypt(byte b)
        {
            i = (i + 1) & 0xFF;
            j = (j + sBox[i] + 1) & 0xFF;
            byte sBoxi = sBox[i];
            byte sBoxj = sBox[j];
            sBox[i] = sBoxj;
            sBox[j] = sBoxi;
            b ^= sBox[(sBoxi + sBoxj) & 0xFF];
            j = (j + b) & 0xFF;
            return b;
        }
        public void Encrypt(byte[] buffer, int offset, int count)
        {
            int il = offset + count;
            for (int ps = offset; ps < il; ps++)
            {
                i = (i + 1) & 0xFF;
                j = (j + sBox[i] + 1) & 0xFF;
                byte sBoxi = sBox[i];
                byte sBoxj = sBox[j];
                sBox[i] = sBoxj;
                sBox[j] = sBoxi;
                j = (j + buffer[ps]) & 0xFF;
                buffer[ps] ^= sBox[(sBoxi + sBoxj) & 0xFF];
            }
        }
        public void Encrypt(byte[] buffer, int offset, int count, Stream OutputStream)
        {
            int il = offset + count;
            for (int ps = offset; ps < il; ps++)
            {
                i = (i + 1) & 0xFF;
                j = (j + sBox[i] + 1) & 0xFF;
                byte sBoxi = sBox[i];
                byte sBoxj = sBox[j];
                sBox[i] = sBoxj;
                sBox[j] = sBoxi;
                j = (j + buffer[ps]) & 0xFF;
                OutputStream.WriteByte((byte)(buffer[ps] ^ sBox[(sBoxi + sBoxj) & 0xFF]));
            }
        }
        public void Decrypt(byte[] buffer, int offset, int count)
        {
            int il = offset + count;
            for (int ps = offset; ps < il; ps++)
            {
                i = (i + 1) & 0xFF;
                j = (j + sBox[i] + 1) & 0xFF;
                byte sBoxi = sBox[i];
                byte sBoxj = sBox[j];
                sBox[i] = sBoxj;
                sBox[j] = sBoxi;
                buffer[ps] ^= sBox[(sBoxi + sBoxj) & 0xFF];
                j = (j + buffer[ps]) & 0xFF;
            }
        }


        internal static RC4CBC CreateFromKey(byte[] KEY, int offset, int count)
        {
            byte[] iBox = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 };
            int j = 0;
            int x = offset;
            int z = offset + count;
            for (int i = 0; i < 256; i++)
            {
                j = (j + iBox[i] + KEY[x++]) & 0xFF;
                if (x == z) x = offset;
                byte temp = iBox[i];
                iBox[i] = iBox[j];
                iBox[j] = temp;
            }
            return new RC4CBC(iBox);
        }
        internal static RC4CBC CreateFromKey(byte[] KEY)
        {
            return CreateFromKey(KEY, 0, KEY.Length);
        }
    }

    #endregion

    #region DES
    internal class DESTransform
    {

        internal static readonly int KEY_BIT_SIZE = 64;
        internal static readonly int KEY_BYTE_SIZE = KEY_BIT_SIZE / 8;
        internal static readonly int BLOCK_BIT_SIZE = 64;
        internal static readonly int BLOCK_BYTE_SIZE = BLOCK_BIT_SIZE / 8;

        private byte[] keySchedule;

        #region pre-computed tables
        // pre-computed result of sBoxes (using pTab)
        private static readonly uint[] spBoxes = { 
														 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000, 
														 0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002, 
														 0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202, 
														 0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000, 
														 0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200, 
														 0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202, 
														 0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200, 
														 0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002, 
														 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010, 
														 0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010, 
														 0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000, 
														 0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010, 
														 0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000, 
														 0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000, 
														 0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010, 
														 0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000, 
														 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100, 
														 0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104, 
														 0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104, 
														 0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000, 
														 0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000, 
														 0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004, 
														 0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004, 
														 0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100, 
														 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000, 
														 0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000, 
														 0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040, 
														 0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040, 
														 0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000, 
														 0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040, 
														 0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040, 
														 0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040, 
														 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000, 
														 0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000, 
														 0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080, 
														 0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080, 
														 0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080, 
														 0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000, 
														 0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000, 
														 0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080, 
														 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000, 
														 0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008, 
														 0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008, 
														 0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000, 
														 0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008, 
														 0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000, 
														 0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008, 
														 0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008, 
														 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400, 
														 0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401, 
														 0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001, 
														 0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400, 
														 0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001, 
														 0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400, 
														 0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401, 
														 0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001, 
														 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000, 
														 0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020, 
														 0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800, 
														 0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000, 
														 0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820, 
														 0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820, 
														 0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000, 
														 0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800
													 };

        // Permuted choice 1 table, PC-1, page 19
        // Translated to zero-based format.
        private static readonly byte[] PC1 = {
													  57-1, 49-1, 41-1, 33-1, 25-1, 17-1,  9-1,
													  1-1, 58-1, 50-1, 42-1, 34-1, 26-1, 18-1,
													  10-1,  2-1, 59-1, 51-1, 43-1, 35-1, 27-1,
													  19-1, 11-1,  3-1, 60-1, 52-1, 44-1, 36-1,
	
													  63-1, 55-1, 47-1, 39-1, 31-1, 23-1, 15-1,
													  7-1, 62-1, 54-1, 46-1, 38-1, 30-1, 22-1,
													  14-1,  6-1, 61-1, 53-1, 45-1, 37-1, 29-1,
													  21-1, 13-1,  5-1, 28-1, 20-1, 12-1,  4-1
												  };

        // pre-computed result of leftRot
        private static readonly byte[] leftRotTotal = { 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x0F, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1C };

        // Permuted choice 2 table, PC-2, page 21
        // Translated to zero-based format.
        private static readonly byte[] PC2 = {
													  14-1, 17-1, 11-1, 24-1,  1-1,  5-1,
													  3-1, 28-1, 15-1,  6-1, 21-1, 10-1,
													  23-1, 19-1, 12-1,  4-1, 26-1,  8-1,
													  16-1,  7-1, 27-1, 20-1, 13-1,  2-1,
													  41-1, 52-1, 31-1, 37-1, 47-1, 55-1,
													  30-1, 40-1, 51-1, 45-1, 33-1, 48-1,
													  44-1, 49-1, 39-1, 56-1, 34-1, 53-1,
													  46-1, 42-1, 50-1, 36-1, 29-1, 32-1
												  };

        internal static readonly uint[] ipTab = {
														0x00000000, 0x00000000, 0x00000100, 0x00000000, 0x00000000, 0x00000100, 0x00000100, 0x00000100, 
														0x00000001, 0x00000000, 0x00000101, 0x00000000, 0x00000001, 0x00000100, 0x00000101, 0x00000100, 
														0x00000000, 0x00000001, 0x00000100, 0x00000001, 0x00000000, 0x00000101, 0x00000100, 0x00000101, 
														0x00000001, 0x00000001, 0x00000101, 0x00000001, 0x00000001, 0x00000101, 0x00000101, 0x00000101, 
														0x00000000, 0x00000000, 0x01000000, 0x00000000, 0x00000000, 0x01000000, 0x01000000, 0x01000000, 
														0x00010000, 0x00000000, 0x01010000, 0x00000000, 0x00010000, 0x01000000, 0x01010000, 0x01000000, 
														0x00000000, 0x00010000, 0x01000000, 0x00010000, 0x00000000, 0x01010000, 0x01000000, 0x01010000, 
														0x00010000, 0x00010000, 0x01010000, 0x00010000, 0x00010000, 0x01010000, 0x01010000, 0x01010000, 
														0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0x00000200, 0x00000200, 0x00000200, 
														0x00000002, 0x00000000, 0x00000202, 0x00000000, 0x00000002, 0x00000200, 0x00000202, 0x00000200, 
														0x00000000, 0x00000002, 0x00000200, 0x00000002, 0x00000000, 0x00000202, 0x00000200, 0x00000202, 
														0x00000002, 0x00000002, 0x00000202, 0x00000002, 0x00000002, 0x00000202, 0x00000202, 0x00000202, 
														0x00000000, 0x00000000, 0x02000000, 0x00000000, 0x00000000, 0x02000000, 0x02000000, 0x02000000, 
														0x00020000, 0x00000000, 0x02020000, 0x00000000, 0x00020000, 0x02000000, 0x02020000, 0x02000000, 
														0x00000000, 0x00020000, 0x02000000, 0x00020000, 0x00000000, 0x02020000, 0x02000000, 0x02020000, 
														0x00020000, 0x00020000, 0x02020000, 0x00020000, 0x00020000, 0x02020000, 0x02020000, 0x02020000, 
														0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000400, 0x00000400, 0x00000400, 
														0x00000004, 0x00000000, 0x00000404, 0x00000000, 0x00000004, 0x00000400, 0x00000404, 0x00000400, 
														0x00000000, 0x00000004, 0x00000400, 0x00000004, 0x00000000, 0x00000404, 0x00000400, 0x00000404, 
														0x00000004, 0x00000004, 0x00000404, 0x00000004, 0x00000004, 0x00000404, 0x00000404, 0x00000404, 
														0x00000000, 0x00000000, 0x04000000, 0x00000000, 0x00000000, 0x04000000, 0x04000000, 0x04000000, 
														0x00040000, 0x00000000, 0x04040000, 0x00000000, 0x00040000, 0x04000000, 0x04040000, 0x04000000, 
														0x00000000, 0x00040000, 0x04000000, 0x00040000, 0x00000000, 0x04040000, 0x04000000, 0x04040000, 
														0x00040000, 0x00040000, 0x04040000, 0x00040000, 0x00040000, 0x04040000, 0x04040000, 0x04040000, 
														0x00000000, 0x00000000, 0x00000800, 0x00000000, 0x00000000, 0x00000800, 0x00000800, 0x00000800, 
														0x00000008, 0x00000000, 0x00000808, 0x00000000, 0x00000008, 0x00000800, 0x00000808, 0x00000800, 
														0x00000000, 0x00000008, 0x00000800, 0x00000008, 0x00000000, 0x00000808, 0x00000800, 0x00000808, 
														0x00000008, 0x00000008, 0x00000808, 0x00000008, 0x00000008, 0x00000808, 0x00000808, 0x00000808, 
														0x00000000, 0x00000000, 0x08000000, 0x00000000, 0x00000000, 0x08000000, 0x08000000, 0x08000000, 
														0x00080000, 0x00000000, 0x08080000, 0x00000000, 0x00080000, 0x08000000, 0x08080000, 0x08000000, 
														0x00000000, 0x00080000, 0x08000000, 0x00080000, 0x00000000, 0x08080000, 0x08000000, 0x08080000, 
														0x00080000, 0x00080000, 0x08080000, 0x00080000, 0x00080000, 0x08080000, 0x08080000, 0x08080000, 
														0x00000000, 0x00000000, 0x00001000, 0x00000000, 0x00000000, 0x00001000, 0x00001000, 0x00001000, 
														0x00000010, 0x00000000, 0x00001010, 0x00000000, 0x00000010, 0x00001000, 0x00001010, 0x00001000, 
														0x00000000, 0x00000010, 0x00001000, 0x00000010, 0x00000000, 0x00001010, 0x00001000, 0x00001010, 
														0x00000010, 0x00000010, 0x00001010, 0x00000010, 0x00000010, 0x00001010, 0x00001010, 0x00001010, 
														0x00000000, 0x00000000, 0x10000000, 0x00000000, 0x00000000, 0x10000000, 0x10000000, 0x10000000, 
														0x00100000, 0x00000000, 0x10100000, 0x00000000, 0x00100000, 0x10000000, 0x10100000, 0x10000000, 
														0x00000000, 0x00100000, 0x10000000, 0x00100000, 0x00000000, 0x10100000, 0x10000000, 0x10100000, 
														0x00100000, 0x00100000, 0x10100000, 0x00100000, 0x00100000, 0x10100000, 0x10100000, 0x10100000, 
														0x00000000, 0x00000000, 0x00002000, 0x00000000, 0x00000000, 0x00002000, 0x00002000, 0x00002000, 
														0x00000020, 0x00000000, 0x00002020, 0x00000000, 0x00000020, 0x00002000, 0x00002020, 0x00002000, 
														0x00000000, 0x00000020, 0x00002000, 0x00000020, 0x00000000, 0x00002020, 0x00002000, 0x00002020, 
														0x00000020, 0x00000020, 0x00002020, 0x00000020, 0x00000020, 0x00002020, 0x00002020, 0x00002020, 
														0x00000000, 0x00000000, 0x20000000, 0x00000000, 0x00000000, 0x20000000, 0x20000000, 0x20000000, 
														0x00200000, 0x00000000, 0x20200000, 0x00000000, 0x00200000, 0x20000000, 0x20200000, 0x20000000, 
														0x00000000, 0x00200000, 0x20000000, 0x00200000, 0x00000000, 0x20200000, 0x20000000, 0x20200000, 
														0x00200000, 0x00200000, 0x20200000, 0x00200000, 0x00200000, 0x20200000, 0x20200000, 0x20200000, 
														0x00000000, 0x00000000, 0x00004000, 0x00000000, 0x00000000, 0x00004000, 0x00004000, 0x00004000, 
														0x00000040, 0x00000000, 0x00004040, 0x00000000, 0x00000040, 0x00004000, 0x00004040, 0x00004000, 
														0x00000000, 0x00000040, 0x00004000, 0x00000040, 0x00000000, 0x00004040, 0x00004000, 0x00004040, 
														0x00000040, 0x00000040, 0x00004040, 0x00000040, 0x00000040, 0x00004040, 0x00004040, 0x00004040, 
														0x00000000, 0x00000000, 0x40000000, 0x00000000, 0x00000000, 0x40000000, 0x40000000, 0x40000000, 
														0x00400000, 0x00000000, 0x40400000, 0x00000000, 0x00400000, 0x40000000, 0x40400000, 0x40000000, 
														0x00000000, 0x00400000, 0x40000000, 0x00400000, 0x00000000, 0x40400000, 0x40000000, 0x40400000, 
														0x00400000, 0x00400000, 0x40400000, 0x00400000, 0x00400000, 0x40400000, 0x40400000, 0x40400000, 
														0x00000000, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00008000, 0x00008000, 0x00008000, 
														0x00000080, 0x00000000, 0x00008080, 0x00000000, 0x00000080, 0x00008000, 0x00008080, 0x00008000, 
														0x00000000, 0x00000080, 0x00008000, 0x00000080, 0x00000000, 0x00008080, 0x00008000, 0x00008080, 
														0x00000080, 0x00000080, 0x00008080, 0x00000080, 0x00000080, 0x00008080, 0x00008080, 0x00008080, 
														0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x80000000, 0x80000000, 0x80000000, 
														0x00800000, 0x00000000, 0x80800000, 0x00000000, 0x00800000, 0x80000000, 0x80800000, 0x80000000, 
														0x00000000, 0x00800000, 0x80000000, 0x00800000, 0x00000000, 0x80800000, 0x80000000, 0x80800000, 
														0x00800000, 0x00800000, 0x80800000, 0x00800000, 0x00800000, 0x80800000, 0x80800000, 0x80800000 
													};

        internal static readonly uint[] fpTab = { 
														0x00000000, 0x00000000, 0x00000000, 0x00000040, 0x00000000, 0x00004000, 0x00000000, 0x00004040, 
														0x00000000, 0x00400000, 0x00000000, 0x00400040, 0x00000000, 0x00404000, 0x00000000, 0x00404040, 
														0x00000000, 0x40000000, 0x00000000, 0x40000040, 0x00000000, 0x40004000, 0x00000000, 0x40004040, 
														0x00000000, 0x40400000, 0x00000000, 0x40400040, 0x00000000, 0x40404000, 0x00000000, 0x40404040, 
														0x00000000, 0x00000000, 0x00000040, 0x00000000, 0x00004000, 0x00000000, 0x00004040, 0x00000000, 
														0x00400000, 0x00000000, 0x00400040, 0x00000000, 0x00404000, 0x00000000, 0x00404040, 0x00000000, 
														0x40000000, 0x00000000, 0x40000040, 0x00000000, 0x40004000, 0x00000000, 0x40004040, 0x00000000, 
														0x40400000, 0x00000000, 0x40400040, 0x00000000, 0x40404000, 0x00000000, 0x40404040, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x00000000, 0x00001000, 0x00000000, 0x00001010, 
														0x00000000, 0x00100000, 0x00000000, 0x00100010, 0x00000000, 0x00101000, 0x00000000, 0x00101010, 
														0x00000000, 0x10000000, 0x00000000, 0x10000010, 0x00000000, 0x10001000, 0x00000000, 0x10001010, 
														0x00000000, 0x10100000, 0x00000000, 0x10100010, 0x00000000, 0x10101000, 0x00000000, 0x10101010, 
														0x00000000, 0x00000000, 0x00000010, 0x00000000, 0x00001000, 0x00000000, 0x00001010, 0x00000000, 
														0x00100000, 0x00000000, 0x00100010, 0x00000000, 0x00101000, 0x00000000, 0x00101010, 0x00000000, 
														0x10000000, 0x00000000, 0x10000010, 0x00000000, 0x10001000, 0x00000000, 0x10001010, 0x00000000, 
														0x10100000, 0x00000000, 0x10100010, 0x00000000, 0x10101000, 0x00000000, 0x10101010, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000400, 0x00000000, 0x00000404, 
														0x00000000, 0x00040000, 0x00000000, 0x00040004, 0x00000000, 0x00040400, 0x00000000, 0x00040404, 
														0x00000000, 0x04000000, 0x00000000, 0x04000004, 0x00000000, 0x04000400, 0x00000000, 0x04000404, 
														0x00000000, 0x04040000, 0x00000000, 0x04040004, 0x00000000, 0x04040400, 0x00000000, 0x04040404, 
														0x00000000, 0x00000000, 0x00000004, 0x00000000, 0x00000400, 0x00000000, 0x00000404, 0x00000000, 
														0x00040000, 0x00000000, 0x00040004, 0x00000000, 0x00040400, 0x00000000, 0x00040404, 0x00000000, 
														0x04000000, 0x00000000, 0x04000004, 0x00000000, 0x04000400, 0x00000000, 0x04000404, 0x00000000, 
														0x04040000, 0x00000000, 0x04040004, 0x00000000, 0x04040400, 0x00000000, 0x04040404, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000100, 0x00000000, 0x00000101, 
														0x00000000, 0x00010000, 0x00000000, 0x00010001, 0x00000000, 0x00010100, 0x00000000, 0x00010101, 
														0x00000000, 0x01000000, 0x00000000, 0x01000001, 0x00000000, 0x01000100, 0x00000000, 0x01000101, 
														0x00000000, 0x01010000, 0x00000000, 0x01010001, 0x00000000, 0x01010100, 0x00000000, 0x01010101, 
														0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000100, 0x00000000, 0x00000101, 0x00000000, 
														0x00010000, 0x00000000, 0x00010001, 0x00000000, 0x00010100, 0x00000000, 0x00010101, 0x00000000, 
														0x01000000, 0x00000000, 0x01000001, 0x00000000, 0x01000100, 0x00000000, 0x01000101, 0x00000000, 
														0x01010000, 0x00000000, 0x01010001, 0x00000000, 0x01010100, 0x00000000, 0x01010101, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000080, 0x00000000, 0x00008000, 0x00000000, 0x00008080, 
														0x00000000, 0x00800000, 0x00000000, 0x00800080, 0x00000000, 0x00808000, 0x00000000, 0x00808080, 
														0x00000000, 0x80000000, 0x00000000, 0x80000080, 0x00000000, 0x80008000, 0x00000000, 0x80008080, 
														0x00000000, 0x80800000, 0x00000000, 0x80800080, 0x00000000, 0x80808000, 0x00000000, 0x80808080, 
														0x00000000, 0x00000000, 0x00000080, 0x00000000, 0x00008000, 0x00000000, 0x00008080, 0x00000000, 
														0x00800000, 0x00000000, 0x00800080, 0x00000000, 0x00808000, 0x00000000, 0x00808080, 0x00000000, 
														0x80000000, 0x00000000, 0x80000080, 0x00000000, 0x80008000, 0x00000000, 0x80008080, 0x00000000, 
														0x80800000, 0x00000000, 0x80800080, 0x00000000, 0x80808000, 0x00000000, 0x80808080, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00002000, 0x00000000, 0x00002020, 
														0x00000000, 0x00200000, 0x00000000, 0x00200020, 0x00000000, 0x00202000, 0x00000000, 0x00202020, 
														0x00000000, 0x20000000, 0x00000000, 0x20000020, 0x00000000, 0x20002000, 0x00000000, 0x20002020, 
														0x00000000, 0x20200000, 0x00000000, 0x20200020, 0x00000000, 0x20202000, 0x00000000, 0x20202020, 
														0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00002000, 0x00000000, 0x00002020, 0x00000000, 
														0x00200000, 0x00000000, 0x00200020, 0x00000000, 0x00202000, 0x00000000, 0x00202020, 0x00000000, 
														0x20000000, 0x00000000, 0x20000020, 0x00000000, 0x20002000, 0x00000000, 0x20002020, 0x00000000, 
														0x20200000, 0x00000000, 0x20200020, 0x00000000, 0x20202000, 0x00000000, 0x20202020, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000008, 0x00000000, 0x00000800, 0x00000000, 0x00000808, 
														0x00000000, 0x00080000, 0x00000000, 0x00080008, 0x00000000, 0x00080800, 0x00000000, 0x00080808, 
														0x00000000, 0x08000000, 0x00000000, 0x08000008, 0x00000000, 0x08000800, 0x00000000, 0x08000808, 
														0x00000000, 0x08080000, 0x00000000, 0x08080008, 0x00000000, 0x08080800, 0x00000000, 0x08080808, 
														0x00000000, 0x00000000, 0x00000008, 0x00000000, 0x00000800, 0x00000000, 0x00000808, 0x00000000, 
														0x00080000, 0x00000000, 0x00080008, 0x00000000, 0x00080800, 0x00000000, 0x00080808, 0x00000000, 
														0x08000000, 0x00000000, 0x08000008, 0x00000000, 0x08000800, 0x00000000, 0x08000808, 0x00000000, 
														0x08080000, 0x00000000, 0x08080008, 0x00000000, 0x08080800, 0x00000000, 0x08080808, 0x00000000, 
														0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000200, 0x00000000, 0x00000202, 
														0x00000000, 0x00020000, 0x00000000, 0x00020002, 0x00000000, 0x00020200, 0x00000000, 0x00020202, 
														0x00000000, 0x02000000, 0x00000000, 0x02000002, 0x00000000, 0x02000200, 0x00000000, 0x02000202, 
														0x00000000, 0x02020000, 0x00000000, 0x02020002, 0x00000000, 0x02020200, 0x00000000, 0x02020202, 
														0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000200, 0x00000000, 0x00000202, 0x00000000, 
														0x00020000, 0x00000000, 0x00020002, 0x00000000, 0x00020200, 0x00000000, 0x00020202, 0x00000000, 
														0x02000000, 0x00000000, 0x02000002, 0x00000000, 0x02000200, 0x00000000, 0x02000202, 0x00000000, 
														0x02020000, 0x00000000, 0x02020002, 0x00000000, 0x02020200, 0x00000000, 0x02020202, 0x00000000 
													};

        #endregion

        #region Static methods
        internal static void Permutation(byte[] input, byte[] output, uint[] permTab, bool preSwap)
        {
            if (preSwap && BitConverter.IsLittleEndian)
                BSwap(input);

            int offs1 = (((int)(input[0]) >> 4)) << 1;
            int offs2 = (1 << 5) + ((((int)input[0]) & 0xF) << 1);

            uint d1 = permTab[offs1++] | permTab[offs2++];
            uint d2 = permTab[offs1] | permTab[offs2];

            int max = BLOCK_BYTE_SIZE << 1;
            for (int i = 2, indx = 1; i < max; i += 2, indx++)
            {
                int ii = (int)input[indx];
                offs1 = (i << 5) + ((ii >> 4) << 1);
                offs2 = ((i + 1) << 5) + ((ii & 0xF) << 1);

                d1 |= permTab[offs1++] | permTab[offs2++];
                d2 |= permTab[offs1] | permTab[offs2];
            }

            if (preSwap || !BitConverter.IsLittleEndian)
            {
                output[0] = (byte)(d1);
                output[1] = (byte)(d1 >> 8);
                output[2] = (byte)(d1 >> 16);
                output[3] = (byte)(d1 >> 24);
                output[4] = (byte)(d2);
                output[5] = (byte)(d2 >> 8);
                output[6] = (byte)(d2 >> 16);
                output[7] = (byte)(d2 >> 24);
            }
            else
            {
                output[0] = (byte)(d1 >> 24);
                output[1] = (byte)(d1 >> 16);
                output[2] = (byte)(d1 >> 8);
                output[3] = (byte)(d1);
                output[4] = (byte)(d2 >> 24);
                output[5] = (byte)(d2 >> 16);
                output[6] = (byte)(d2 >> 8);
                output[7] = (byte)(d2);
            }
        }

        private static void BSwap(byte[] byteBuff)
        {
            byte t = byteBuff[0];
            byteBuff[0] = byteBuff[3];
            byteBuff[3] = t;

            t = byteBuff[1];
            byteBuff[1] = byteBuff[2];
            byteBuff[2] = t;

            t = byteBuff[4];
            byteBuff[4] = byteBuff[7];
            byteBuff[7] = t;

            t = byteBuff[5];
            byteBuff[5] = byteBuff[6];
            byteBuff[6] = t;
        }

        #endregion

        #region Constructor
        // Default constructor.
        internal DESTransform(byte[] key)
        {
            if (key == null || key.Length != 8) throw new ArgumentException("Invalid DES key");
            keySchedule = new byte[KEY_BYTE_SIZE * 16];
            SetKey(key);
        }
        internal void SetKey(byte[] key)
        {
            // NOTE: see Fig. 3, Key schedule calculation, at page 20.
            Array.Clear(keySchedule, 0, keySchedule.Length);

            int keyBitSize = PC1.Length;

            byte[] keyPC1 = new byte[keyBitSize]; // PC1-permuted key
            byte[] keyRot = new byte[keyBitSize]; // PC1 & rotated

            for (int x = 0; x < PC1.Length; x++)
            {
                byte bitPos = PC1[x];
                keyPC1[x] = (byte)((key[(int)bitPos >> 3] >> (7 ^ (bitPos & 7))) & 1);
            }

            int j;
            for (int i = 0; i < KEY_BYTE_SIZE * 2; i++)
            {
                int b = keyBitSize >> 1;

                for (j = 0; j < b; j++)
                {
                    int s = j + (int)leftRotTotal[i];
                    keyRot[j] = keyPC1[s < b ? s : s - b];
                }

                for (j = b; j < keyBitSize; j++)
                {
                    int s = j + (int)leftRotTotal[i];
                    keyRot[j] = keyPC1[s < keyBitSize ? s : s - b];
                }

                int keyOffs = i * KEY_BYTE_SIZE;

                for (j = 0; j < PC2.Length; j++)
                    if (keyRot[PC2[j]] != 0) keySchedule[keyOffs + (j / 6)] |= (byte)(0x80 >> ((j % 6) + 2));
            }
        }
        #endregion

        #region CipherFunct
        private uint CipherFunct(uint r, int n)
        {
            uint res = 0;
            byte[] subkey = keySchedule;
            int i = n << 3;

            uint rt = (r >> 1) | (r << 31); // ROR32(r)
            res |= spBoxes[0 * 64 + (((rt >> 26) ^ subkey[i++]) & 0x3F)];
            res |= spBoxes[1 * 64 + (((rt >> 22) ^ subkey[i++]) & 0x3F)];
            res |= spBoxes[2 * 64 + (((rt >> 18) ^ subkey[i++]) & 0x3F)];
            res |= spBoxes[3 * 64 + (((rt >> 14) ^ subkey[i++]) & 0x3F)];
            res |= spBoxes[4 * 64 + (((rt >> 10) ^ subkey[i++]) & 0x3F)];
            res |= spBoxes[5 * 64 + (((rt >> 6) ^ subkey[i++]) & 0x3F)];
            res |= spBoxes[6 * 64 + (((rt >> 2) ^ subkey[i++]) & 0x3F)];
            rt = (r << 1) | (r >> 31); // ROL32(r)
            res |= spBoxes[7 * 64 + ((rt ^ subkey[i]) & 0x3F)];
            return res;
        }
        #endregion

        #region ProcessBlock

        // private helper for TripleDES
        private void ProcessBlock(byte[] input, byte[] output, bool encrypt)
        {
            uint[] dwordBuff = new uint[BLOCK_BYTE_SIZE / 4];
            Buffer.BlockCopy(input, 0, dwordBuff, 0, BLOCK_BYTE_SIZE);

            if (encrypt)
            {
                uint d0 = dwordBuff[0];
                uint d1 = dwordBuff[1];

                // 16 rounds
                d0 ^= CipherFunct(d1, 0);
                d1 ^= CipherFunct(d0, 1);
                d0 ^= CipherFunct(d1, 2);
                d1 ^= CipherFunct(d0, 3);
                d0 ^= CipherFunct(d1, 4);
                d1 ^= CipherFunct(d0, 5);
                d0 ^= CipherFunct(d1, 6);
                d1 ^= CipherFunct(d0, 7);
                d0 ^= CipherFunct(d1, 8);
                d1 ^= CipherFunct(d0, 9);
                d0 ^= CipherFunct(d1, 10);
                d1 ^= CipherFunct(d0, 11);
                d0 ^= CipherFunct(d1, 12);
                d1 ^= CipherFunct(d0, 13);
                d0 ^= CipherFunct(d1, 14);
                d1 ^= CipherFunct(d0, 15);

                dwordBuff[0] = d1;
                dwordBuff[1] = d0;
            }
            else
            {
                uint d1 = dwordBuff[0];
                uint d0 = dwordBuff[1];

                // 16 rounds in reverse order
                d1 ^= CipherFunct(d0, 15);
                d0 ^= CipherFunct(d1, 14);
                d1 ^= CipherFunct(d0, 13);
                d0 ^= CipherFunct(d1, 12);
                d1 ^= CipherFunct(d0, 11);
                d0 ^= CipherFunct(d1, 10);
                d1 ^= CipherFunct(d0, 9);
                d0 ^= CipherFunct(d1, 8);
                d1 ^= CipherFunct(d0, 7);
                d0 ^= CipherFunct(d1, 6);
                d1 ^= CipherFunct(d0, 5);
                d0 ^= CipherFunct(d1, 4);
                d1 ^= CipherFunct(d0, 3);
                d0 ^= CipherFunct(d1, 2);
                d1 ^= CipherFunct(d0, 1);
                d0 ^= CipherFunct(d1, 0);

                dwordBuff[0] = d0;
                dwordBuff[1] = d1;
            }

            Buffer.BlockCopy(dwordBuff, 0, output, 0, BLOCK_BYTE_SIZE);
        }
        #endregion

        internal byte[] ECB(byte[] input, bool encrypt)
        {
            byte[] output = new byte[8];
            Permutation(input, output, ipTab, false);
            byte[] byteBuff = new byte[BLOCK_BYTE_SIZE];
            ProcessBlock(output, byteBuff, encrypt);
            Permutation(byteBuff, output, fpTab, true);
            return output;
        }
        internal void ECB(byte[] input, byte[] output, bool encrypt)
        {
            Permutation(input, output, ipTab, false);
            byte[] byteBuff = new byte[BLOCK_BYTE_SIZE];
            ProcessBlock(output, byteBuff, encrypt);
            Permutation(byteBuff, output, fpTab, true);
        }
        internal void ECB(byte[] input, int offset, byte[] output, bool encrypt)
        {
            byte[] block = new byte[8];
            Buffer.BlockCopy(input, offset, block, 0, offset + 8 > input.Length ? input.Length - offset : 8);
            Permutation(block, output, ipTab, false);
            byte[] byteBuff = new byte[BLOCK_BYTE_SIZE];
            ProcessBlock(output, byteBuff, encrypt);
            Permutation(byteBuff, output, fpTab, true);
        }

        #region Static DES/3DES cryptography methods

        #region CBC
        public static byte[] CBC(byte[] KEY, byte[] input, bool encrypt)
        {
            int count = input.Length;
            byte[] output = new byte[(count & 7) != 0 ? (count | 7) + 1 : count];
            CBC(KEY, input, 0, output, 0, count, encrypt);
            return output;
        }
        public static byte[] CBC(byte[] KEY, byte[] input, int inOffset, int count, bool encrypt)
        {
            byte[] output = new byte[(count & 7) != 0 ? (count | 7) + 1 : count];
            CBC(KEY, input, inOffset, output, 0, count, encrypt);
            return output;
        }
        public static void CBC(byte[] KEY, byte[] input, int inOffset, byte[] output, int outOffset, int count, bool encrypt)
        {
            if (KEY == null) throw new ArgumentNullException();
            switch (KEY.Length)
            {
                case 8:
                    {
                        #region 64 bit DES key
                        byte[] temp = new byte[BLOCK_BYTE_SIZE];
                        DESTransform DT1 = new DESTransform(KEY);
                        if (encrypt)
                        {
                            for (int ofs = inOffset; ofs < inOffset + count; ofs += 8)
                            {
                                for (int i = 0; i < BLOCK_BYTE_SIZE; i++) temp[i] ^= ofs + i < inOffset + count ? input[ofs + i] : (byte)0;
                                Permutation(temp, temp, ipTab, false);
                                DT1.ProcessBlock(temp, temp, true);
                                Permutation(temp, temp, fpTab, true);
                                Buffer.BlockCopy(temp, 0, output, outOffset + ofs - inOffset, BLOCK_BYTE_SIZE);
                            }
                        }
                        else
                        {
                            byte[] temp2 = new byte[8];
                            for (int ofs = inOffset; ofs < inOffset + count; ofs += 8)
                            {
                                Buffer.BlockCopy(input, ofs, temp, 0, 8);
                                Permutation(temp, temp, ipTab, false);
                                DT1.ProcessBlock(temp, temp, false);
                                Permutation(temp, temp, fpTab, true);
                                for (int i = 0; i < BLOCK_BYTE_SIZE; i++) output[outOffset + ofs - inOffset + i] = (byte)(temp2[i] ^ temp[i]);
                                Buffer.BlockCopy(input, ofs, temp2, 0, 8);
                            }
                        }
                        return;
                        #endregion
                    }
                case 16:
                    {
                        #region 128 bit tripple des key
                        byte[] temp = new byte[BLOCK_BYTE_SIZE];
                        DESTransform DT1 = new DESTransform(new byte[] { KEY[0], KEY[1], KEY[2], KEY[3], KEY[4], KEY[5], KEY[6], KEY[7] });
                        DESTransform DT2 = new DESTransform(new byte[] { KEY[8], KEY[9], KEY[10], KEY[11], KEY[12], KEY[13], KEY[14], KEY[15] });
                        if (encrypt)
                        {
                            for (int ofs = inOffset; ofs < inOffset + count; ofs += 8)
                            {
                                for (int i = 0; i < BLOCK_BYTE_SIZE; i++) temp[i] ^= ofs + i < inOffset + count ? input[ofs + i] : (byte)0;
                                Permutation(temp, temp, ipTab, false);
                                DT1.ProcessBlock(temp, temp, true);	// encrypt
                                DT2.ProcessBlock(temp, temp, false);	// decrypt
                                DT1.ProcessBlock(temp, temp, true);	// encrypt
                                Permutation(temp, temp, fpTab, true);
                                Buffer.BlockCopy(temp, 0, output, outOffset + ofs - inOffset, BLOCK_BYTE_SIZE);
                            }
                        }
                        else
                        {
                            byte[] temp2 = new byte[8];
                            for (int ofs = inOffset; ofs < inOffset + count; ofs += 8)
                            {
                                Buffer.BlockCopy(input, ofs, temp, 0, 8);
                                Permutation(temp, temp, ipTab, false);
                                DT1.ProcessBlock(temp, temp, false);	// decrypt
                                DT2.ProcessBlock(temp, temp, true);	// encrypt
                                DT1.ProcessBlock(temp, temp, false);	// decrypt
                                Permutation(temp, temp, fpTab, true);
                                for (int i = 0; i < BLOCK_BYTE_SIZE; i++) output[outOffset + ofs - inOffset + i] = (byte)(temp2[i] ^ temp[i]);
                                Buffer.BlockCopy(input, ofs, temp2, 0, 8);
                            }
                        }
                        return;
                        #endregion
                    }
                case 24:
                    {
                        #region 192 bit tripple des key
                        byte[] temp = new byte[BLOCK_BYTE_SIZE];
                        DESTransform DT1 = new DESTransform(new byte[] { KEY[0], KEY[1], KEY[2], KEY[3], KEY[4], KEY[5], KEY[6], KEY[7] });
                        DESTransform DT2 = new DESTransform(new byte[] { KEY[8], KEY[9], KEY[10], KEY[11], KEY[12], KEY[13], KEY[14], KEY[15] });
                        DESTransform DT3 = new DESTransform(new byte[] { KEY[16], KEY[17], KEY[18], KEY[19], KEY[20], KEY[21], KEY[22], KEY[23] });
                        if (encrypt)
                        {
                            for (int ofs = inOffset; ofs < inOffset + count; ofs += 8)
                            {
                                for (int i = 0; i < BLOCK_BYTE_SIZE; i++) temp[i] ^= ofs + i < inOffset + count ? input[ofs + i] : (byte)0;
                                Permutation(temp, temp, ipTab, false);
                                DT1.ProcessBlock(temp, temp, true);		// encrypt
                                DT2.ProcessBlock(temp, temp, false);		// decrypt
                                DT3.ProcessBlock(temp, temp, true);		// encrypt
                                Permutation(temp, temp, fpTab, true);
                                Buffer.BlockCopy(temp, 0, output, outOffset + ofs - inOffset, BLOCK_BYTE_SIZE);
                            }
                        }
                        else
                        {
                            byte[] temp2 = new byte[8];
                            for (int ofs = inOffset; ofs < inOffset + count; ofs += 8)
                            {
                                Buffer.BlockCopy(input, ofs, temp, 0, 8);
                                Permutation(temp, temp, ipTab, false);
                                DT3.ProcessBlock(temp, temp, false);	// decrypt
                                DT2.ProcessBlock(temp, temp, true);	// encrypt
                                DT1.ProcessBlock(temp, temp, false);	// decrypt
                                Permutation(temp, temp, fpTab, true);
                                for (int i = 0; i < BLOCK_BYTE_SIZE; i++) output[outOffset + ofs - inOffset + i] = (byte)(temp2[i] ^ temp[i]);
                                Buffer.BlockCopy(input, ofs, temp2, 0, 8);
                            }

                        }
                        return;
                        #endregion
                    }
                default: throw new ArgumentException("Invalid DES/3DES key length");
            }
        }
        #endregion

        #region EBC
        public static byte[] EBC(byte[] KEY, byte[] input, bool encrypt)
        {
            byte[] output = new byte[8];
            CBC(KEY, input, 0, output, 0, 8, encrypt);
            return output;
        }
        public static byte[] EBC(byte[] KEY, byte[] input, int inOffset, bool encrypt)
        {
            byte[] output = new byte[8];
            CBC(KEY, input, inOffset, output, 0, 8, encrypt);
            return output;
        }
        public static void EBC(byte[] KEY, byte[] input, int inOffset, byte[] output, int outOffset, bool encrypt)
        {
            if (KEY == null) throw new ArgumentNullException();
            switch (KEY.Length)
            {
                case 8:
                    {
                        #region 64 bit DES key
                        byte[] temp = new byte[BLOCK_BYTE_SIZE];
                        DESTransform DT1 = new DESTransform(KEY);
                        if (encrypt)
                        {
                            for (int i = 0; i < BLOCK_BYTE_SIZE; i++) temp[i] ^= i < BLOCK_BYTE_SIZE ? input[inOffset + i] : (byte)0;
                            Permutation(temp, temp, ipTab, false);
                            DT1.ProcessBlock(temp, temp, true);
                            Permutation(temp, temp, fpTab, true);
                            Buffer.BlockCopy(temp, 0, output, outOffset, BLOCK_BYTE_SIZE);
                        }
                        else
                        {
                            Buffer.BlockCopy(input, inOffset, temp, 0, 8);
                            Permutation(temp, temp, ipTab, false);
                            DT1.ProcessBlock(temp, temp, false);
                            Permutation(temp, temp, fpTab, true);
                            for (int i = 0; i < BLOCK_BYTE_SIZE; i++) output[outOffset + i] = (byte)temp[i];
                        }
                        return;
                        #endregion
                    }
                case 16:
                    {
                        #region 128 bit tripple des key
                        byte[] temp = new byte[BLOCK_BYTE_SIZE];
                        DESTransform DT1 = new DESTransform(new byte[] { KEY[0], KEY[1], KEY[2], KEY[3], KEY[4], KEY[5], KEY[6], KEY[7] });
                        DESTransform DT2 = new DESTransform(new byte[] { KEY[8], KEY[9], KEY[10], KEY[11], KEY[12], KEY[13], KEY[14], KEY[15] });
                        if (encrypt)
                        {
                            for (int i = 0; i < BLOCK_BYTE_SIZE; i++) temp[i] ^= i < BLOCK_BYTE_SIZE ? input[inOffset + i] : (byte)0;
                            Permutation(temp, temp, ipTab, false);
                            DT1.ProcessBlock(temp, temp, true);	// encrypt
                            DT2.ProcessBlock(temp, temp, false);	// decrypt
                            DT1.ProcessBlock(temp, temp, true);	// encrypt
                            Permutation(temp, temp, fpTab, true);
                            Buffer.BlockCopy(temp, 0, output, outOffset, BLOCK_BYTE_SIZE);
                        }
                        else
                        {
                            Buffer.BlockCopy(input, inOffset, temp, 0, 8);
                            Permutation(temp, temp, ipTab, false);
                            DT1.ProcessBlock(temp, temp, false);	// decrypt
                            DT2.ProcessBlock(temp, temp, true);	// encrypt
                            DT1.ProcessBlock(temp, temp, false);	// decrypt
                            Permutation(temp, temp, fpTab, true);
                            for (int i = 0; i < BLOCK_BYTE_SIZE; i++) output[outOffset + i] = temp[i];
                        }
                        return;
                        #endregion
                    }
                case 24:
                    {
                        #region 192 bit tripple des key
                        byte[] temp = new byte[BLOCK_BYTE_SIZE];
                        DESTransform DT1 = new DESTransform(new byte[] { KEY[0], KEY[1], KEY[2], KEY[3], KEY[4], KEY[5], KEY[6], KEY[7] });
                        DESTransform DT2 = new DESTransform(new byte[] { KEY[8], KEY[9], KEY[10], KEY[11], KEY[12], KEY[13], KEY[14], KEY[15] });
                        DESTransform DT3 = new DESTransform(new byte[] { KEY[16], KEY[17], KEY[18], KEY[19], KEY[20], KEY[21], KEY[22], KEY[23] });
                        if (encrypt)
                        {
                            for (int i = 0; i < BLOCK_BYTE_SIZE; i++) temp[i] ^= i < BLOCK_BYTE_SIZE ? input[inOffset + i] : (byte)0;
                            Permutation(temp, temp, ipTab, false);
                            DT1.ProcessBlock(temp, temp, true);		// encrypt
                            DT2.ProcessBlock(temp, temp, false);		// decrypt
                            DT3.ProcessBlock(temp, temp, true);		// encrypt
                            Permutation(temp, temp, fpTab, true);
                            Buffer.BlockCopy(temp, 0, output, outOffset, BLOCK_BYTE_SIZE);
                        }
                        else
                        {
                            Buffer.BlockCopy(input, inOffset, temp, 0, 8);
                            Permutation(temp, temp, ipTab, false);
                            DT3.ProcessBlock(temp, temp, false);	// decrypt
                            DT2.ProcessBlock(temp, temp, true);	// encrypt
                            DT1.ProcessBlock(temp, temp, false);	// decrypt
                            Permutation(temp, temp, fpTab, true);
                            for (int i = 0; i < BLOCK_BYTE_SIZE; i++) output[outOffset + i] = temp[i];
                        }
                        return;
                        #endregion
                    }
                default: throw new ArgumentException("Invalid DES/3DES key length");
            }
        }
        #endregion

        #endregion
    }

    #endregion

    #region CBC XOR
    public static class CBCXor
    {
        public static void DoCBCDeXor(byte[] data, byte[] password)
        {
            /*
	            DWORD pos = 1;
	            int prev = data[0];
	            int tmp;
	            data[0] ^= password[0];
	            for (DWORD i = 1; i < size; i++)
	            {
		            tmp = prev;
		            prev = data[i];
		            data[i] ^= tmp ^ password[pos++];
		            if (pos >= pass_size)
			            pos = 0;
	            }
            */
            if (data == null || data.Length == 0)
                return;
            int pos = 1;
            byte prev = data[0];
            for (int i = 1; i < data.Length; i++)
            {
                byte tmp = prev;
                prev = data[i];
                data[i] ^= (byte)(tmp ^ password[pos++]);
                if (pos >= password.Length)
                    pos = 0;
            }
        }

        public static void DoCBCXor(byte[] msg, byte[] password)
        {
            /*
            DWORD pos = 1;
            data[0] ^= password[0];
            for (DWORD i = 1; i < size; i++)
            {
                data[i] = data[i - 1] ^ data[i] ^ password[pos++];
                if (pos >= pass_size)
                    pos = 0;
            }
            */
            if (msg == null || msg.Length == 0)
                return;
            int pos = 1;
            msg[0] ^= password[0];
            for (int i = 1; i < msg.Length; i++)
            {
                msg[i] = (byte)(msg[i - 1] ^ msg[i] ^ password[pos++]);
                if (pos >= password.Length)
                    pos = 0;
            }
        }

    }
    #endregion

    #region Int32 STACK
    /// <summary>
    /// This clas provides Int32Type stack(LiFo) for faster unboxed pushing and pulling.
    /// </summary>
    public sealed class Int32Stack
    {
        private Int32 ps = 0;
        private Int32[] ii;
        /// <summary>
        /// Basic Int32 Stack constructor
        /// </summary>
        /// <param name="size">Maximum stack size</param>
        public Int32Stack(Int32 size)
        {
            ii = new Int32[size];
        }

        /// <summary>
        /// Reurns number of integer values stored in stack
        /// </summary>
        public Int32 Count
        {
            get 
            {
                return ps;
            }
        }
        /// <summary>
        /// Pushes new integer value to stack
        /// </summary>
        /// <param name="i">Value to pusj</param>
        public void Push(Int32 i)
        {
            if (ps >= ii.Length) throw new Exception("Stack is full");
            ii[ps++] = i;
        }
        /// <summary>
        /// Retrievs stored integer value from stack
        /// </summary>
        /// <returns>Value from stack</returns>
        public Int32 Pop()
        {
            if (ps == 0) throw new Exception("Stack is empty");
            return ii[--ps];
        }

    }
    #endregion

    #region Element/Key=Value Stream
    #region EKV Writer

    /// <summary>
    /// Element key/value stream writer provides fast forward only writing of Element Node with Key/value pairs tree.
    /// </summary>
    public sealed class EKVWriter
    {
        /// <summary>
        /// Transforms XML data to Element tree key/value stream.
        /// </summary>
        /// <param name="xr">XmlTextReader</param>
        /// <returns>byte[] EKV encoded data</returns>
        public static byte[] XML2EKV(XmlTextReader xr)
        {
            EKVWriter wr = new EKVWriter(256);
            Int32 d = xr.Depth;
            while (xr.MoveToNextAttribute())
            {
                wr.Write(xr.Name, xr.Value);
            }
            while (xr.Read() && xr.Depth > d)
            {
                if (xr.NodeType == XmlNodeType.Element)
                {
                    wr.Start(xr.Name);
                    bool empty = xr.IsEmptyElement;
                    while (xr.MoveToNextAttribute()) wr.Write(xr.Name, xr.Value);
                    if (empty) wr.End();
                }
                else if (xr.NodeType == XmlNodeType.EndElement) wr.End();
            }
            return wr.ToArray();
        }

        /// <summary>
        /// Creates writer with specified initial buffer size
        /// </summary>
        /// <param name="initialSize">Initial buffer size</param>
        public EKVWriter(Int32 initialSize)
        {
            buf = new byte[initialSize];
        }

        private byte[] buf;
        private Int32 ps = 0;
        private static Encoding UTF8 = Encoding.UTF8;
        private static Encoding ASCII = Encoding.ASCII;

        /// <summary>
        /// Returns byte array representing encoded data.
        /// </summary>
        /// <returns>ByteArray EKV encoded data</returns>
        public byte[] ToArray()
        {
            byte[] ba = new byte[ps];
            Buffer.BlockCopy(buf, 0, ba, 0, ps);
            return ba;
        }
        /// <summary>
        /// Starts new element with specifick name. Name length must fit ASCII encoding cannot exceed length of 31 characters.
        /// </summary>
        /// <param name="ElelementName">Element name</param>
        public void Start(string ElelementName)
        {
            if (ElelementName == null || ElelementName.Length ==0) throw new Exception();
            byte[] en = ASCII.GetBytes(ElelementName);
            if (en.Length > 31) throw new Exception();
            if (ps+en.Length+1 > buf.Length)
            {
                byte[] _buf = buf;
                buf = new byte[ps+en.Length+128];
                Buffer.BlockCopy(_buf, 0, buf, 0, ps);
            }
            buf[ps++] = (byte)en.Length;
            Buffer.BlockCopy(en, 0, buf, ps, en.Length);
            ps += en.Length;
        }
        /// <summary>
        /// Writes end marking of current element.
        /// </summary>
        public void End()
        {
            if (ps+1 > buf.Length)
            {
                byte[] _buf = buf;
                buf = new byte[ps+128];
                Buffer.BlockCopy(_buf, 0, buf, 0, ps);
            }
            buf[ps++] = 0;
        }
        /// <summary>
        /// Writes Key/Value pair
        /// </summary>
        /// <param name="Key">Key - cannot be null</param>
        /// <param name="Value">Value - can be null</param>
        public void Write(string Key, string Value)
        {
            if (Key == null || Key.Length == 0) throw new Exception();
            byte[] k = ASCII.GetBytes(Key);
            byte[] v = Value == null ? null : UTF8.GetBytes(Value);
            Int32 mnl = 4 +k.Length + (v == null ? 0 : v.Length);
            if (ps + mnl > buf.Length)
            {
                byte[] _buf = buf;
                buf = new byte[ps+mnl+128];
                Buffer.BlockCopy(_buf, 0, buf, 0, ps);
            }
            if (v == null)
            {
                buf[ps++] = (byte)(0x80 | (k.Length & 0x0F));
                Buffer.BlockCopy(k, 0, buf, ps, k.Length);
                ps += k.Length;
            }
            else if (v.Length > 0xFFFF)
            {
                buf[ps++] = (byte)(0xB0 | (k.Length & 0x0F));
                Buffer.BlockCopy(k, 0, buf, ps, k.Length);
                ps += k.Length;
                buf[ps++] = (byte)((v.Length >> 16) & 0xFF);
                buf[ps++] = (byte)((v.Length >> 8) & 0xFF);
                buf[ps++] = (byte)(v.Length & 0xFF);
                Buffer.BlockCopy(v, 0, buf, ps, v.Length);
                ps += v.Length;
            }
            else if (v.Length > 0xFF)
            {
                buf[ps++] = (byte)(0xA0 | (k.Length & 0x0F));
                Buffer.BlockCopy(k, 0, buf, ps, k.Length);
                ps += k.Length;
                buf[ps++] = (byte)((v.Length >> 8) & 0xFF);
                buf[ps++] = (byte)(v.Length & 0xFF);
                Buffer.BlockCopy(v, 0, buf, ps, v.Length);
                ps += v.Length;
            }
            else 
            {
                buf[ps++] = (byte)(0x90 | (k.Length & 0x0F));
                Buffer.BlockCopy(k, 0, buf, ps, k.Length);
                ps += k.Length;
                buf[ps++] = (byte)v.Length;
                Buffer.BlockCopy(v, 0, buf, ps, v.Length);
                ps += v.Length;
            }
        }
    }

    #endregion

    #region EKV Reader
    /// <summary>
    /// Element key/value fast forward only reader.
    /// </summary>
    public sealed class EKVReader
    {
        /// <summary>
        /// Checks if given byte array is actualy valid EKV data.
        /// </summary>
        /// <param name="buf">EKV encoded data</param>
        /// <returns>True if data is EKV encoded</returns>
        public static bool Validate(byte[] buf)
        {
            return Validate(buf, 0, buf.Length);
        }
        /// <summary>
        /// Checks if given byte array is actualy valid EKV data.
        /// </summary>
        /// <param name="buf">EKV encoded data</param>
        /// <param name="offset">Data Offset</param>
        /// <param name="count">Data Count</param>
        /// <returns>True if data is EKV encoded</returns>
        public static bool Validate(byte[] buf, Int32 offset, Int32 count)
        {
            Int32 ps = offset;
            Int32 end = offset + count;
            Int32 depth = 0;
            if (end > buf.Length) return false;
            while (ps < end) 
            {
                byte b = buf[ps++];
                if (b == 0)
                {
                    depth--;
                    if (depth < 0) return false;
                }
                else if ((b & 0x80) == 0)
                {
                    depth++;
                    ps += b & 0x1F;
                }
                else
                {
                    ps += b & 0x0F;
                    Int32 lbc = (b & 0x30) >> 4;
                    Int32 len = 0;
                    for (Int32 i = 0; i < lbc; i++) len = (len << 8) + buf[ps++];
                    ps += len;
                }
            }
            return ps == end;
        }
        /// <summary>
        /// Creates EKV reader on given buffer
        /// </summary>
        /// <param name="Buffer">EKV encoedd data</param>
        public EKVReader(byte[] Buffer)
        {
            buf = Buffer;
            end = Buffer.Length;
        }
        private byte[] buf;
        private Int32 end = 0;
        private Int32 ps = 0;
        /// <summary>
        /// Current tree depth
        /// </summary>
        public Int32 Depth 
        {
            get { return enl.Count; }
        }
        private string _element = null;
        /// <summary>
        /// Current element name
        /// </summary>
        public string Element
        {
            get 
            {
                return _element;
            }
        }
        private string _path = "/";
        /// <summary>
        /// Full path of current element
        /// </summary>
        public string Path
        {
            get 
            {
                return _path;
            }
        }
        private Int32Stack enl = new Int32Stack(10);
        private static Encoding UTF8 = Encoding.UTF8;
        private static Encoding ASCII = Encoding.ASCII;
        /// <summary>
        /// End of stream
        /// </summary>
        public bool EOS	{	get { return ps >= end; }	}
        /// <summary>
        /// Moves to next element.
        /// </summary>
        /// <returns>True if successfull</returns>
        public bool Next()
        {
            while (ps < end) 
            {
                byte b = buf[ps++];
                if (b == 0)
                {
                    _path = _path.Substring(0, _path.Length - enl.Pop());
                }
                else if ((b & 0x80) == 0)
                {
                    _element = ASCII.GetString(buf, ps, b & 0x1F);
                    _path += _element+'/';
                    ps += b & 0x1F;
                    enl.Push(_element.Length+1);
                    return true;
                }
                else
                {
                    ps += b & 0x0F;
                    Int32 lbc = (b & 0x30) >> 4;
                    Int32 len = 0;
                    for (Int32 i = 0; i < lbc; i++) len = (len << 8) + buf[ps++];
                    ps += len;
                }
            }
            return false;
        }
        /// <summary>
        /// Moves to next element at the given path
        /// </summary>
        /// <param name="Path">Path to search</param>
        /// <returns>True if successfull</returns>
        public bool Next(string Path)
        {
            while (ps < end) 
            {
                byte b = buf[ps++];
                if (b == 0)
                {
                    _path = _path.Substring(0, _path.Length - enl.Pop());
                }
                else if ((b & 0x80) == 0)
                {
                    string en = ASCII.GetString(buf, ps, b & 0x1F);
                    _path += en+'/';
                    ps += b & 0x1F;
                    enl.Push(en.Length+1);
                    if (_path == Path) return true;
                }
                else
                {
                    ps += b & 0x0F;
                    Int32 lbc = (b & 0x30) >> 4;
                    Int32 len = 0;
                    for (Int32 i = 0; i < lbc; i++) len = (len << 8) + buf[ps++];
                    ps += len;
                }
            }
            return false;
        }

        /// <summary>
        /// Reads key/value pair
        /// </summary>
        /// <param name="Key">Key - cannot be null</param>
        /// <param name="Value">Value - cann be null</param>
        /// <returns>True if successfull</returns>
        public bool Read(out string Key, out string Value)
        {
            if (ps < end && (buf[ps] & 0x80) != 0)
            {
                byte b = buf[ps++];
                Key = ASCII.GetString(buf, ps, b & 0x0F);
                ps += b & 0x0F;
                Int32 lbc = (b & 0x30) >> 4;
                if (lbc > 0)
                {
                    Int32 len = 0;
                    for (Int32 i = 0; i < lbc; i++) len = (len << 8) + buf[ps++];
                    Value = UTF8.GetString(buf, ps, len);
                    ps += len;
                }
                else Value = null;
                return true;
            }
            else 
            {
                Key = null; 
                Value = null;
                return false;
            }
        }

        /// <summary>
        /// Reads value for a particual key
        /// </summary>
        /// <param name="Key">Key - cannot be null</param>
        /// <returns>Value (Null if key not found)</returns>
        public string Read(string Key)
        {
            int ps = this.ps;
            while (ps < end && (buf[ps] & 0x80) != 0)
            {
                byte b = buf[ps++];
                string key = ASCII.GetString(buf, ps, b & 0x0F);
                ps += b & 0x0F;
                Int32 lbc = (b & 0x30) >> 4;
                if (lbc > 0)
                {
                    Int32 len = 0;
                    for (Int32 i = 0; i < lbc; i++) len = (len << 8) + buf[ps++];
                    if (key == Key) return UTF8.GetString(buf, ps, len);
                    ps += len;
                }
                else if (key == Key) return null;
            }
            return null;
        }
        /// <summary>
        /// Reads end element marking.
        /// </summary>
        /// <returns>True if marking was found and readed successfully</returns>
        public bool ReadEnd()
        {
            if (ps < end && buf[ps] == 0)
            {
                ps ++;
                return true;
            }
            else return false;
        }

    }
    #endregion
    #endregion

    #region SDO
	
    #region SDO writer
    /// <summary>
    /// Streamed data objects writer
    /// </summary>
    public sealed class SDOWriter
    {
        #region Variables
        private int _is;
        private int _ps = 0;
        private byte[] _buf;
        #endregion 

        #region Constructors
        /// <summary>
        /// Creates streamed data objects writer with initial buffer size
        /// </summary>
        /// <param name="InitialSize">Buffer size in bytes</param>
        public SDOWriter(int InitialSize)
        {
            _buf = new byte[_is = InitialSize > 64 ? InitialSize : 64 ];
        }
        #endregion

        #region Public members
        /// <summary>
        /// Length of data
        /// </summary>
        public int Length{ get { return _ps; } }

        #region Write

        #region Write constructed

        /// <summary>
        /// Writes raw data
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="buffer">Buffer</param>
        public void WriteRAW(ulong TAG, byte[] buffer)
        {
            if (TAG == 0) throw new ArgumentException();
            if (buffer == null) return;
            WriteRAW(TAG, buffer, 0, buffer.Length);
        }
        /// <summary>
        /// Writes raw data
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        public void WriteRAW(ulong TAG, byte[] buffer, int offset, int count)
        {
            if (TAG == 0) throw new ArgumentException();
            if (count <= 0) throw new ArgumentException();
            if (buffer == null) return;

            if (_ps+count+10 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+count+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);
            _buf[_ps++] = 0x00;									// constucted element
            Buffer.BlockCopy(buffer,offset, _buf, _ps, count);	// raw sdo data
            _ps+=count;
            _buf[_ps++] = 0x00;									// end of constucted element tag

        }

        /// <summary>
        /// Write start of constructed TAG
        /// </summary>
        /// <param name="TAG">TAG</param>
        public void WriteStart(ulong TAG)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+9 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);					

            _buf[_ps++] = 0x00;									// constucted element
        }
        /// <summary>
        /// Write end of constructed TAG
        /// </summary>
        public void WriteEnd()
        {
            if (_ps+1 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            _buf[_ps++] = 0x00;									// end of constucted element tag
        }

        #endregion

        #region Write Guid
        /// <summary>
        /// Write Guid
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="guid">Value</param>
        public void Write(ulong TAG, Guid guid)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+25 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x50;
            Buffer.BlockCopy(guid.ToByteArray(), 0, _buf, _ps, 16);
            _ps += 16;
        }

        #endregion

        #region Write Bool
        /// <summary>
        /// Write bool
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="b">Value</param>
        public void Write(ulong TAG, bool b)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+9 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);
            _buf[_ps++] = b ? (byte)0x20 : (byte)0x40;
        }

        #endregion

        #region Write unsigned integer
        /// <summary>
        /// Write byte
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="b">Value</param>
        public void Write(ulong TAG, byte b)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+10 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x41;
            _buf[_ps++] = b;

        }
        /// <summary>
        /// Write UInt16
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="i">Value</param>
        public void Write(ulong TAG, UInt16 i)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+11 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x42;
            _buf[_ps++] = (byte)((i >> 8) & 0xFF);
            _buf[_ps++] = (byte)(i & 0xFF);
        }
        /// <summary>
        /// Write UInt32
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="i">Value</param>		
        public void Write(ulong TAG, UInt32 i)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+13 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x44;
            _buf[_ps++] = (byte)((i >> 24) & 0xFF);
            _buf[_ps++] = (byte)((i >> 16) & 0xFF);
            _buf[_ps++] = (byte)((i >> 8) & 0xFF);
            _buf[_ps++] = (byte)(i & 0xFF);
        }
        /// <summary>
        /// Write UInt64
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="i">Value</param>		
        public void Write(ulong TAG, UInt64 i)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+17 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x48;
            _buf[_ps++] = (byte)((i >> 56) & 0xFF);
            _buf[_ps++] = (byte)((i >> 48) & 0xFF);
            _buf[_ps++] = (byte)((i >> 40) & 0xFF);
            _buf[_ps++] = (byte)((i >> 32) & 0xFF);
            _buf[_ps++] = (byte)((i >> 24) & 0xFF);
            _buf[_ps++] = (byte)((i >> 16) & 0xFF);
            _buf[_ps++] = (byte)((i >> 8) & 0xFF);
            _buf[_ps++] = (byte)(i & 0xFF);
        }
        #endregion

        #region Write DateTime
        /// <summary>
        /// Write Date Time
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="dt">Value</param>
        public void Write(ulong TAG, DateTime dt)
        {
            Write(TAG, dt.Ticks);
        }
        #endregion

        #region Write signed integer
        /// <summary>
        /// Write Int16
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="i">Value</param>		
        public void Write(ulong TAG, Int16 i)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+11 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x22;
            _buf[_ps++] = (byte)((i >> 8) & 0xFF);
            _buf[_ps++] = (byte)(i & 0xFF);
        }
        /// <summary>
        /// Write Int32
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="i">Value</param>		
        public void Write(ulong TAG, Int32 i)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+13 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x24;
            _buf[_ps++] = (byte)((i >> 24) & 0xFF);
            _buf[_ps++] = (byte)((i >> 16) & 0xFF);
            _buf[_ps++] = (byte)((i >> 8) & 0xFF);
            _buf[_ps++] = (byte)(i & 0xFF);
        }
        /// <summary>
        /// Write Int64
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="i">Value</param>		
        public void Write(ulong TAG, Int64 i)
        {
            if (TAG == 0) throw new ArgumentException();
            if (_ps+17 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            _buf[_ps++] = 0x28;
            _buf[_ps++] = (byte)((i >> 56) & 0xFF);
            _buf[_ps++] = (byte)((i >> 48) & 0xFF);
            _buf[_ps++] = (byte)((i >> 40) & 0xFF);
            _buf[_ps++] = (byte)((i >> 32) & 0xFF);
            _buf[_ps++] = (byte)((i >> 24) & 0xFF);
            _buf[_ps++] = (byte)((i >> 16) & 0xFF);
            _buf[_ps++] = (byte)((i >> 8) & 0xFF);
            _buf[_ps++] = (byte)(i & 0xFF);
        }
        #endregion

        #region Write byte[]
        /// <summary>
        /// Write byte array
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="buffer">Buffer</param>	
        public void Write(ulong TAG, byte[] buffer)
        {
            if (TAG == 0) throw new ArgumentException();
            if (buffer == null) return;
            Write(TAG, buffer, 0, buffer.Length);
        }
        /// <summary>
        /// Write byte array
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        public void Write(ulong TAG, byte[] buffer, int offset, int count)
        {
            if (TAG == 0) throw new ArgumentException();
            if (count < 0) throw new ArgumentException();
            if (buffer == null) return;

            if (_ps+count+17 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+count+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            if (count <= 0x10)	{ _buf[_ps++] = (byte)count;		}
            else if(count <= 0xFF) { _buf[_ps++] = 0x11; _buf[_ps++] = (byte)count; }
            else if(count <= 0xFFFF) {_buf[_ps++] = 0x12; _buf[_ps++] = (byte)((count >> 8) & 0xFF); _buf[_ps++] = (byte)(count & 0xFF); }
            else if(count <= 0xFFFFFF) {_buf[_ps++] = 0x13; _buf[_ps++] = (byte)((count >> 16) & 0xFF); _buf[_ps++] = (byte)((count >> 8) & 0xFF); _buf[_ps++] = (byte)(count & 0xFF); }
            else {_buf[_ps++] = 0x14; _buf[_ps++] = (byte)((count >> 24) & 0xFF); _buf[_ps++] = (byte)((count >> 16) & 0xFF); _buf[_ps++] = (byte)((count >> 8) & 0xFF); _buf[_ps++] = (byte)(count & 0xFF); }

            Buffer.BlockCopy(buffer,offset, _buf, _ps, count);
            _ps+=count;
        }
        #endregion

        #region Write string
        /// <summary>
        /// Write String
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="s">Value</param>
        public void Write(ulong TAG, string s)
        {
            if (TAG == 0) throw new ArgumentException();
            if (s == null) return;
            byte[] buffer = Encoding.UTF8.GetBytes(s);
            int count = buffer.Length;

            if (_ps+count+17 > _buf.Length)
            {
                byte[] buf = _buf;
                _buf = new byte[_ps+count+_is];
                Buffer.BlockCopy(buf, 0, _buf, 0, _ps);
            }
            if (TAG > 0xFFFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 56) & 0xFF);
            if (TAG > 0xFFFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 48) & 0xFF);
            if (TAG > 0xFFFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 40) & 0xFF);
            if (TAG > 0xFFFFFFFF) _buf[_ps++] = (byte)((TAG >> 32) & 0xFF);
            if (TAG > 0xFFFFFF) _buf[_ps++] = (byte)((TAG >> 24) & 0xFF);
            if (TAG > 0xFFFF) _buf[_ps++] = (byte)((TAG >> 16) & 0xFF);
            if (TAG > 0xFF) _buf[_ps++] = (byte)((TAG >> 8) & 0xFF);
            _buf[_ps++] = (byte)(TAG & 0xFF);

            if (count <= 0x10)	{ _buf[_ps++] = (byte)(0x60 + count);		}
            else if(count <= 0xFF) { _buf[_ps++] = 0x71; _buf[_ps++] = (byte)count; }
            else if(count <= 0xFFFF) {_buf[_ps++] = 0x72; _buf[_ps++] = (byte)((count >> 8) & 0xFF); _buf[_ps++] = (byte)(count & 0xFF); }
            else if(count <= 0xFFFFFF) {_buf[_ps++] = 0x73; _buf[_ps++] = (byte)((count >> 16) & 0xFF); _buf[_ps++] = (byte)((count >> 8) & 0xFF); _buf[_ps++] = (byte)(count & 0xFF); }
            else {_buf[_ps++] = 0x74; _buf[_ps++] = (byte)((count >> 24) & 0xFF); _buf[_ps++] = (byte)((count >> 16) & 0xFF); _buf[_ps++] = (byte)((count >> 8) & 0xFF); _buf[_ps++] = (byte)(count & 0xFF); }

            Buffer.BlockCopy(buffer,0, _buf, _ps, count);
            _ps+=count;
        }
        #endregion

        #endregion

        /// <summary>
        /// To byte array
        /// </summary>
        /// <returns>Byte array</returns>
        public byte[] ToArray()
        {
            if (_buf == null || _ps == 0) return null;
            byte[] ba = new byte[_ps];
            Buffer.BlockCopy(_buf, 0, ba,0, _ps);
            return ba;
        }
        /// <summary>
        /// Flushes data to stream
        /// </summary>
        /// <param name="str">Stream</param>
        public void Flush (Stream str)
        {
            if (_buf == null || _ps == 0) return;
            str.Write(_buf, 0, _ps);
        }

        #endregion

    }

    #endregion

    #region SDO reader
    /// <summary>
    /// Streamed data objects reader
    /// </summary>
    public sealed class SDOReader
    {
        #region Variables
        private byte[] _buf;
        private int _ps;
        private int _e;
        private ulong _tag = 0;
        private Stack _stack = new Stack();
        internal bool ontag = true;
        
        /// <summary>
        /// If string is not in UTF-8 encoding
        /// </summary>
        Encoding m_encSrc = Encoding.UTF8;

        #endregion

        public SDOReader(byte[] buffer)
            : this(buffer, 0, buffer.Length)
        {
        }

        /// <summary>
        /// Create SDO Reader from buffer
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        public SDOReader(byte[] buffer, int offset, int count)
        {
            _buf = buffer;
            _ps = offset;
            _e = offset + count;
        }

        #region Public members

        /// <summary>
        /// Encoding for non-unicode strings in SDO
        /// </summary>
        public Encoding Encoding
        {
            set { m_encSrc = value; }
            get { return m_encSrc; }
        }

        /// <summary>
        /// Current TAG
        /// </summary>
        public ulong TAG	{ get { return _tag; }	}
        /// <summary>
        /// Current Depth
        /// </summary>
        public int Depth	{ get { return _stack.Count; }	}
        /// <summary>
        /// End of file/stream
        /// </summary>
        public bool EOF		{ get { return _ps >= _e;	}	}
        /// <summary>
        /// Is constructed object
        /// </summary>
        public bool IsConstructedObject{	get { return _ps < _e && _buf[_ps] == 0;	}	}

        #region Value Reading

        /// <summary>
        /// Read byte array
        /// </summary>
        /// <returns>ByteArray</returns>
        public byte[] ReadByteArray()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            //if ((_buf[_ps] & 0xE0) != 0 || (_buf[_ps] & 0x1F) == 0) throw new Exception("Value not byte array.");;
            if ((_buf[_ps] & 0xE0) != 0) throw new Exception("Value not byte array.");;
            int len = _buf[_ps++] & 0x1F;
            if (len > 16)
            {
                int x = len & 0x0F;
                len = 0;
                for (int i = 0; i < x; i++) len = (len << 8) + _buf[_ps++];
            }
            if (len > 52428800 ) throw new Exception("Byte array too large. (50Mb limit)");
            byte[] ba = new byte[len];
            Buffer.BlockCopy(_buf, _ps, ba, 0, len);
            _ps+=len;
            ontag = true;
            return ba;
        }
        /// <summary>
        /// Read string
        /// </summary>
        /// <returns>string</returns>
        public string ReadString()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            if ((_buf[_ps] & 0xE0) != 0x60) throw new Exception("Value not string.");;
            int len = _buf[_ps++] & 0x1F;
            if (len > 16)
            {
                int x = len & 0x0F;
                len = 0;
                for (int i = 0; i < x; i++) len = (len << 8) + _buf[_ps++];
            }
            if (len > 52428800 ) throw new Exception("String size too large. (50Mb limit)");
            _ps+=len;
            ontag = true;

            if( m_encSrc != Encoding.UTF8 )
            {
                byte[] utf8Enc = Encoding.Convert(m_encSrc, Encoding.UTF8, _buf, _ps - len, len);
                //Log4cs.Log(Importance.Debug, "UTF-8: {0}", Encoding.UTF8.GetString(utf8Enc));
                return Encoding.UTF8.GetString(utf8Enc);
            }
            
            return Encoding.UTF8.GetString(_buf, _ps-len, len);
        }
        /// <summary>
        /// Read DateTime
        /// </summary>
        /// <returns>DateTime</returns>
        public DateTime ReadDateTime()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            if (_buf[_ps] != 0x28) throw new Exception("Value not DateTime compatible.");
            Int64 i = 0;
            int x = _buf[_ps++] & 0x0F;
            for (int ck = 0; ck < x; ck ++)	{	i <<= 8;	i |= _buf[_ps++];	}
            ontag = true;
            return new DateTime(i);
        }
        /// <summary>
        /// Read Int64
        /// </summary>
        /// <returns>Int64</returns>
        public Int64 ReadInt()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            if (_buf[_ps] != 0x21 && _buf[_ps] != 0x22 && _buf[_ps] != 0x24 && _buf[_ps] != 0x28) throw new Exception("Value not Int.");
            Int64 i = 0;
            int x = _buf[_ps++] & 0x0F;
            for (int ck = 0; ck < x; ck ++)	{	i <<= 8;	i |= _buf[_ps++];	}
            ontag = true;
            return i;
        }
        /// <summary>
        /// Read UInt64
        /// </summary>
        /// <returns>UInt64</returns>
        public UInt64 ReadUInt()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            if (_buf[_ps] != 0x41 && _buf[_ps] != 0x42 && _buf[_ps] != 0x44 && _buf[_ps] != 0x48) throw new Exception("Value not UInt.");
            UInt64 i = 0;
            int x = _buf[_ps++] & 0x0F;
            for (int ck = 0; ck < x; ck ++)	{	i <<= 8;	i |= _buf[_ps++];	}
            ontag = true;
            return i;
        }
        /// <summary>
        /// Read Guid
        /// </summary>
        /// <returns>Guid</returns>
        public Guid ReadGuid()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            if (_buf[_ps] != 0x50) throw new Exception("Value not Guid.");
            _ps++;
            byte[] ba = new byte[16];
            Buffer.BlockCopy(_buf, _ps, ba, 0, 16);
            _ps += 16;
            ontag = true;
            return new Guid(ba);
        }

        /// <summary>
        /// Read bool
        /// </summary>
        /// <returns>bool</returns>
        public bool ReadBool()
        {
            if (ontag) throw new Exception("SDO reader error. Current possition not on value.");
            if (_buf[_ps] != 0x20 && _buf[_ps] != 0x40) throw new Exception("Value not bool.");
            ontag = true;
            return _buf[_ps++] == 0x20;
        }

        /// <summary>
        /// Read value
        /// </summary>
        /// <returns>object</returns>
        public object ReadValue()
        {
            if( ontag ) throw new Exception("SDO reader error. Current possition not on value.");
            if( _buf[_ps] == 0 ) return null;
            switch( _buf[_ps] >> 5 )
            {
                case 0: return ReadByteArray();
                case 1:
                    switch( _buf[_ps] )
                    {
                        case 0x20: ontag = true; _ps++; return true;	// bool true
                        case 0x21: ontag = true; _ps++; return (sbyte)_buf[_ps++];	// int8 ???
                        case 0x22: // int16
                            {
                                _ps++; Int32 i = 0;
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                ontag = true;
                                return (Int16)i;
                            }
                        case 0x24:
                            {
                                _ps++; Int32 i = 0;
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                ontag = true;
                                return i;
                            }
                        case 0x28:
                            {
                                _ps++; Int64 i = 0;
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                ontag = true;
                                return i;
                            }
                        default: return null;
                    }
                case 2:
                    switch( _buf[_ps] )
                    {
                        case 0x40: ontag = true; _ps++; return false;	// bool false
                        case 0x41: ontag = true; _ps++; return _buf[_ps++];	// byte
                        case 0x42: // uint16
                            {
                                _ps++; UInt16 i = 0;
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                ontag = true;
                                return i;
                            }
                        case 0x44:
                            {
                                _ps++; UInt32 i = 0;
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                ontag = true;
                                return i;
                            }
                        case 0x48:
                            {
                                _ps++; UInt64 i = 0;
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                i <<= 8; i |= _buf[_ps++]; i <<= 8; i |= _buf[_ps++];
                                ontag = true;
                                return i;
                            }
                        case 0x50:
                            {
                                _ps++;
                                byte[] ba = new byte[16];
                                Buffer.BlockCopy(_buf, _ps, ba, 0, 16);
                                _ps += 16;
                                ontag = true;
                                return new Guid(ba);
                            }
                        default: return null;
                    }
                case 3: return ReadString();
                default: return null;
            }
        }

        #endregion

        #region Skip TAG value
        /// <summary>
        /// Skip TAG value
        /// </summary>
        public void Skip()
        {
            if (!ontag)
            {
                byte b = _buf[_ps++];
                if (b == 0)
                {
                    int dp = 1;
                    while (dp > 0)
                    {
                        ulong tag = _buf[_ps++];
                        if (tag == 0) { dp--; continue; }
                        if (tag > 0xF8) _ps += (int)(tag & 7);
                        int x = _buf[_ps++];
                        if (x == 0) { dp++; continue; }
                        if ((x & 0x1F) > 16)
                        {
                            int len = 0;
                            for (int i = 0; i < (x & 0x0F); i++) len = (len << 8) + _buf[_ps++];
                            _ps += len;
                        }
                        else _ps += x & 0x1F;
                    }
                }
                else if ((b & 0x1F) > 16)
                {
                    int len = 0;
                    for (int i = 0; i < (b & 0x0F); i++) len = (len << 8) + _buf[_ps++];
                    _ps += len;
                }
                else _ps += b & 0x1F;
                ontag = true;
            }
        }
        #endregion

        #region Read Tag

        /// <summary>
        /// Read TAG but do not advance possition
        /// </summary>
        /// <returns>TAG</returns>
        public ulong PeekTag()
        {
            if (ontag)
            {
                int ps = _ps;
                if (ps >= _e) return 0;
                ulong tag = _buf[ps++];
                if (tag == 0) return 0;
                if (tag > 0xF8) 
                {
                    int x = (int)(tag & 7);
                    for (int i = 0; i < x; i ++) tag = (tag << 8) + _buf[ps++];
                }
                return tag;
            }
            else return 0;
        }

        /// <summary>
        /// Read TAG
        /// </summary>
        /// <returns>True if TAG readed succesfuly. False if EOF or End of custructed element</returns>
        public bool ReadTag()
        {
            if (_ps >= _e) return false;
            if (!ontag) 
            {
                if (_buf[_ps] == 0)
                {
                    _stack.Push(_tag);
                    _ps++;
                    ontag = true;
                }
                else Skip();
            }
            if (_ps >= _e) return false;
            ulong tag = _buf[_ps++];
            if (tag == 0)
            {
                _tag = (ulong)_stack.Pop();
                ontag = true;
                return false;
            }
            if (tag > 0xF8) 
            {
                int x = (int)(tag & 7);
                for (int i = 0; i < x; i ++) tag = (tag << 8) + _buf[_ps++];
            }
            _tag = tag;
            ontag = false;
            return true;
        }
        #endregion

        #endregion

        /// <summary>
        /// This static methods validates SDO encoded data
        /// </summary>
        /// <param name="buf">SDO encoded data</param>
        /// <returns>True if data will be parsed without exceptions</returns>
        public static bool Validate(byte[] buf)
        {
            if (buf == null || buf.Length == 0) return false;
            Int32 ps = 0;
            Int32 dp = 0;
            while (dp >= 0 && ps < buf.Length)
            {
                UInt64 tag = buf[ps++];
                if (tag == 0) { dp--; continue; }
                if (tag > 0xF8) ps += (Int32)(tag & 7);
                if (ps >= buf.Length) return false;	// invalid data cannot be here !!!
                Int32 x = buf[ps++];
                if (x == 0) { dp++; continue; }
                if ((x & 0x1F) > 16)
                {
                    Int32 len = 0;
                    Int32 lb = x & 0x0F;
                    if (ps+lb >= buf.Length) return false;
                    for (int i = 0; i < lb; i++) len = (len << 8) + buf[ps++];
                    ps += len;
                }
                else ps += x & 0x1F;
            }
            return ps == buf.Length;
        }

    }
	
    #endregion

    #endregion

    #region BERTLV parse/compose


    /// <summary>
    /// BERTLV data parser
    /// </summary>
    public class BERTLV
    {
        #region Static methods
        /// <summary>
        /// Get TAG value
        /// </summary>
        /// <param name="TAG">TAG</param>
        /// <param name="iccdata">BERTLV encoded data</param>
        /// <returns>Value</returns>
        public static byte[] Get(ulong TAG, byte[] iccdata)
        {
            if (iccdata == null) return null;
            int ps = 0;
            while (ps < iccdata.Length)
            {
                ulong tag = iccdata[ps++];
                if ((tag & 0x1F) == 0x1F)
                    while (ps < iccdata.Length )
                        if (((tag = (tag << 8) + iccdata[ps++]) & 0x80) == 0) break;
                byte b = iccdata[ps++];
                uint len = 0;
                if ((b & 0x80) == 1) for (int i = 0; i < b; i++) len = (len << 8)  + iccdata[ps++];
                else  len = b;
                if (tag == TAG)
                {
                    byte[] val = new byte[len];
                    for (uint i = 0; i < len; i++) val[i] = iccdata[ps++];
                    return val;
                } 
                else ps += (int)len;
            }
            return null;
        }
        #endregion


        #region search tree
        private class TE
        {
            internal byte splitByte;
            internal bool isKey = false;
            internal TE parent;
            internal TE lowChild = null;
            internal TE eqChild = null;
            internal TE highChild = null;

            internal ulong TAG;
            internal byte[] KEY;
            internal byte[] VAL;

            public TE(TE parent, byte splitByte)
            {
                this.parent = parent;
                this.splitByte = splitByte;
            }
        }

        private void Set(byte[] Key, byte[] VAL, ulong TAG)
        {
            if (root == null)	root = new TE(null,Key[0]);
            TE p = root;
            int i = 0;
            byte b;
            while (i<Key.Length)
            {
                b = Key[i];
                if (b < p.splitByte)		p = (p.lowChild==null)	? (p.lowChild	= new TE(p,b)) :p.lowChild;
                else if (b > p.splitByte)	p = (p.highChild==null)	? (p.highChild	= new TE(p,b)) : p.highChild;
                else if (++i < Key.Length)	p = (p.eqChild==null)	? (p.eqChild	= new TE(p,Key[i])) : p.eqChild;
            }
            p.isKey=true;
            p.TAG = TAG;
            p.KEY = Key;
            p.VAL = VAL;

        }
        private byte[] Get(byte[] Key)
        {
            int n = Key.Length;
            TE p = root;	
            int i=0;
            byte b;
            while(i<n && p!=null)
            {
                b = Key[i];
                if (b < p.splitByte)		p=p.lowChild;
                else if (b > p.splitByte)	p=p.highChild;
                else
                {
                    ++i;
                    if (p.isKey && i == n) return p.VAL;
                    p=p.eqChild;
                }		
            }
            return null;
        } 
        private TE root;
        #endregion

        /// <summary>
        /// Creates BERTLV parsed from buffer
        /// </summary>
        /// <param name="ByteArray">Buffer</param>
        public BERTLV(byte[] ByteArray)
        {
            if (ByteArray == null) throw new ArgumentNullException();
            int ps = 0;
            Hashtable ht = new Hashtable();
            while (ps < ByteArray.Length)
            {
                int x = ps;
                ulong TAG = ByteArray[ps++];
                if ((TAG & 0x1F) == 0x1F)
                    while (ps < ByteArray.Length )
                        if (((TAG = (TAG << 8) + ByteArray[ps++]) & 0x80) == 0) break;
                byte[] KEY = new byte[ps - x];
                Buffer.BlockCopy(ByteArray, x, KEY, 0, KEY.Length);
                byte b = ByteArray[ps++];
                uint LEN = 0;
                if ((b & 0x80) == 1) for (int i = 0; i < b; i++) LEN = (LEN << 8)  + ByteArray[ps++];
                else  LEN = b;
                byte[] VAL = new byte[LEN];
                for (uint i = 0; i < LEN; i++) VAL[i] = ByteArray[ps++];
                Set(KEY, VAL, TAG);
            }
        }

        /// <summary>
        /// TAG List
        /// </summary>
        public ICollection TagList
        {
            get 
            {
                ArrayList al = new ArrayList();
                Stack stack = new Stack();
                stack.Push(root);
                while (stack.Count > 0)
                {
                    TE p = (TE)stack.Pop();
                    if (p.highChild != null) stack.Push(p.highChild);
                    if (p.eqChild != null) stack.Push(p.eqChild);
                    if (p.lowChild != null) stack.Push(p.lowChild);
                    if (p.isKey) al.Add(p.TAG);
                }
                return al;
            }
        }
        /// <summary>
        /// Get/Set TAG value
        /// </summary>
        public byte[] this[ulong TAG]
        {
            get
            {
                byte[] KEY = 
                    (TAG <= 0xFF) ?	new byte[] {(byte)(TAG & 0xFF)} : 
                    (TAG <= 0xFFFF) ? new byte[] {(byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} : 
                    (TAG <= 0xFFFFFF) ? new byte[] {(byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} : 
                    (TAG <= 0xFFFFFFFF) ? new byte[] {(byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    (TAG <= 0xFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    (TAG <= 0xFFFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    (TAG <= 0xFFFFFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 48) & 0xFF), (byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    new byte[] {(byte)((TAG >> 56) & 0xFF), (byte)((TAG >> 48) & 0xFF), (byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)};
                return Get(KEY);
            }
            set 
            {
                byte[] KEY = 
                    (TAG <= 0xFF) ?	new byte[] {(byte)(TAG & 0xFF)} : 
                    (TAG <= 0xFFFF) ? new byte[] {(byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} : 
                    (TAG <= 0xFFFFFF) ? new byte[] {(byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} : 
                    (TAG <= 0xFFFFFFFF) ? new byte[] {(byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    (TAG <= 0xFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    (TAG <= 0xFFFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    (TAG <= 0xFFFFFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 48) & 0xFF), (byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                    new byte[] {(byte)((TAG >> 56) & 0xFF), (byte)((TAG >> 48) & 0xFF), (byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)};
                Set(KEY, value, TAG);
            }
        }

        /// <summary>
        /// Creates byte array of BERTLV encoded data
        /// </summary>
        /// <param name="TAGlist">TAG list to use for encoding</param>
        /// <returns>BERTLV encoded data</returns>
        public byte[] ToByteArray(params ulong[] TAGlist)
        {
            if (root == null) return null;
            MemoryStream ms = new MemoryStream();
            if (TAGlist == null || TAGlist.Length == 0)
            {
                Stack stack = new Stack();
                stack.Push(root);
                while (stack.Count > 0)
                {
                    TE p = (TE)stack.Pop();
                    if (p.highChild != null) stack.Push(p.highChild);
                    if (p.eqChild != null) stack.Push(p.eqChild);
                    if (p.lowChild != null) stack.Push(p.lowChild);
                    if (p.isKey) 
                    {
                        byte[] VAL = p.VAL;
                        if (VAL != null && VAL.Length != 0)
                        {
                            ms.Write(p.KEY, 0, p.KEY.Length);
                            byte[] len = 
                                (VAL.Length < 0x80) ? new byte[] {(byte)VAL.Length} : 
                                (VAL.Length <= 0xFF) ? new byte[] {0x81, (byte)VAL.Length} : 
                                (VAL.Length <= 0xFFFF) ? new byte[] {0x82, (byte)((VAL.Length >> 8) & 0xFF), (byte)(VAL.Length & 0xFF)} : 
                                (VAL.Length <= 0xFFFFFF) ? new byte[] {0x83, (byte)((VAL.Length >> 16) & 0xFF), (byte)((VAL.Length >> 8) & 0xFF), (byte)(VAL.Length & 0xFF)} : 
                                new byte[] {0x84, (byte)((VAL.Length >> 24) & 0x7F), (byte)((VAL.Length >> 16) & 0xFF), (byte)((VAL.Length >> 8) & 0xFF), (byte)(VAL.Length & 0xFF)};
                            ms.Write(len, 0, len.Length);
                            ms.Write(VAL,0,VAL.Length);
                        }				
                    }
                }
            }
            else 
            {
                for (int j = 0; j < TAGlist.Length; j++)
                {
                    ulong TAG = TAGlist[j];
                    byte[] Key = 
                        (TAG <= 0xFF) ?	new byte[] {(byte)(TAG & 0xFF)} : 
                        (TAG <= 0xFFFF) ? new byte[] {(byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} : 
                        (TAG <= 0xFFFFFF) ? new byte[] {(byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} : 
                        (TAG <= 0xFFFFFFFF) ? new byte[] {(byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                        (TAG <= 0xFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                        (TAG <= 0xFFFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                        (TAG <= 0xFFFFFFFFFFFFFF) ? new byte[] {(byte)((TAG >> 48) & 0xFF), (byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)} :
                        new byte[] {(byte)((TAG >> 56) & 0xFF), (byte)((TAG >> 48) & 0xFF), (byte)((TAG >> 40) & 0xFF), (byte)((TAG >> 32) & 0xFF), (byte)((TAG >> 24) & 0xFF), (byte)((TAG >> 16) & 0xFF), (byte)((TAG >> 8) & 0xFF),(byte)(TAG & 0xFF)};

                    int n = Key.Length;
                    TE p = root;	
                    int i=0;
                    byte b;
                    while(i<n && p!=null)
                    {
                        b = Key[i];
                        if (b < p.splitByte)		p=p.lowChild;
                        else if (b > p.splitByte)	p=p.highChild;
                        else
                        {
                            ++i;
                            if (p.isKey && i == n) 
                            {
                                byte[] VAL = p.VAL;
                                if (VAL != null && VAL.Length != 0)
                                {
                                    ms.Write(p.KEY, 0, p.KEY.Length);
                                    byte[] len = 
                                        (VAL.Length < 0x80) ? new byte[] {(byte)VAL.Length} : 
                                        (VAL.Length <= 0xFF) ? new byte[] {0x81, (byte)VAL.Length} : 
                                        (VAL.Length <= 0xFFFF) ? new byte[] {0x82, (byte)((VAL.Length >> 8) & 0xFF), (byte)(VAL.Length & 0xFF)} : 
                                        (VAL.Length <= 0xFFFFFF) ? new byte[] {0x83, (byte)((VAL.Length >> 16) & 0xFF), (byte)((VAL.Length >> 8) & 0xFF), (byte)(VAL.Length & 0xFF)} : 
                                        new byte[] {0x84, (byte)((VAL.Length >> 24) & 0x7F), (byte)((VAL.Length >> 16) & 0xFF), (byte)((VAL.Length >> 8) & 0xFF), (byte)(VAL.Length & 0xFF)};
                                    ms.Write(len, 0, len.Length);
                                    ms.Write(VAL,0,VAL.Length);
                                }
                                break;
                            }
                            p=p.eqChild;
                        }		
                    }

                }
            }
            return ms.Length > 0 ? ms.ToArray() : null;
        }
    }

    #endregion

    #region Extended memory stream


    /// <summary>
    /// Memory stream with extra methods for easer ISO8583 document creation
    /// </summary>
    public class MemoryStreamISO8583Ex : MemoryStreamEx
    {
        private long _bitmapoffset = 0;
        private int	_bitmaplength = 0;
        private bool _bitmapinhex = false;

        #region Bitmap writing

        /// <summary>
        /// Writes ISO8583 bitmap at a current possition of memory stream.
        /// </summary>
        /// <param name="length">Bitmap length in bits (64 or 128 bits only)</param>
        public void WriteISO8583BitMap(int length)
        {
            WriteISO8583BitMap(length, false);
        }
        /// <summary>
        /// Writes ISO8583 bitmap at current possition and will be updated as new ISO8583 fields will be added.
        /// </summary>
        /// <param name="length">Bitmap length. Valid values(64/128)</param>
        /// <param name="HexEncoded">True if bitmap should be represented as hex encoded (used for ASCII formated documents)</param>
        public void WriteISO8583BitMap(int length, bool HexEncoded)
        {
            if (length != 64 && length != 128) throw new Exception("Incorrect bitmap length");
            if (_bitmaplength != 0) throw new Exception("Bitmap already mapped");
            _bitmapoffset = Position;
            _bitmaplength = length;
            _bitmapinhex = HexEncoded;
            if (!_bitmapinhex)
            {
                byte[] ba = new byte[length  >> 3];
                if (length == 128) ba[0] = 128;
                base.Write(ba, 0, ba.Length);
            }
            else 
            {
                byte[] ba = new byte[length >> 2];
                for (int i = 0; i < ba.Length; i++) ba[i] = 48;
                if (length == 128) ba[0] = (byte)'8';
                base.Write(ba, 0, ba.Length);
            }
        }
        private static char[] HX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        private void SetBit(int bit)
        {
            if (bit > _bitmaplength || bit < 1) throw new Exception("Bitmap index out of bounds");
            long ps = Position;
            try 
            {
                bit--;
                if (!_bitmapinhex)
                {
                    int ofs = (bit >> 3);
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    byte b = (byte)((128 >> (bit & 7)) | ReadByte());
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    WriteByte(b);
                }
                else 
                {
                    int ofs = (bit >> 2);
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    char c = (char)ReadByte();
                    Seek(_bitmapoffset + ofs, SeekOrigin.Begin);
                    byte b = (byte)((8 >> (bit & 3)));
                    switch (c)
                    {
                        case '0' : b |= 0x0; break;
                        case '1' : b |= 0x1; break;
                        case '2' : b |= 0x2; break;
                        case '3' : b |= 0x3; break;
                        case '4' : b |= 0x4; break;
                        case '5' : b |= 0x5; break;
                        case '6' : b |= 0x6; break;
                        case '7' : b |= 0x7; break;
                        case '8' : b |= 0x8; break;
                        case '9' : b |= 0x9; break;
                        case 'A' : b |= 0xA; break;
                        case 'B' : b |= 0xB; break;
                        case 'C' : b |= 0xC; break;
                        case 'D' : b |= 0xD; break;
                        case 'E' : b |= 0xE; break;
                        case 'F' : b |= 0xF; break;
                    }
                    WriteByte((byte)HX[b]);
                }
            }
            finally {	Seek(ps, SeekOrigin.Begin);	}
        }

        /// <summary>
        /// Write element
        /// </summary>
        /// <param name="Length">Length</param>
        /// <param name="buffer">Buffer</param>
        /// <param name="bit">Field number</param>
        public void Write(string Length, byte[] buffer, int bit)
        {
            base.WriteString(Length);
            base.Write(buffer, 0, buffer.Length);
            SetBit(bit);
        }

        /// <summary>
        /// Write element
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        /// <param name="bit">Field number</param>
        public void Write(byte[] buffer, int offset, int count, int bit)
        {
            base.Write(buffer, offset, count);
            SetBit(bit);
        }
        /// <summary>
        /// Write element
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="bit">Field number</param>
        public void Write(byte[] buffer, int bit)
        {
            base.Write(buffer);
            SetBit(bit);
        }
        /// <summary>
        /// Write string as BCD encoded data
        /// </summary>
        /// <param name="s">Value</param>
        /// <param name="bit">Field number</param>
        public void WriteBcdEncoded(string s, int bit)
        {
            base.WriteBcdEncoded(s);
            SetBit(bit);
        }
        /// <summary>
        /// Write string 
        /// </summary>
        /// <param name="s">Value</param>
        /// <param name="bit">Field number</param>
        public void WriteString(string s, int bit)
        {
            base.WriteString(s);
            SetBit(bit);
        }
        /// <summary>
        /// Write buffer data as HEX encoded string (used for ASCII encoded ISO8583 documents)
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="bit">Field number</param>
        public void WriteHexEncoded(byte[] buffer, int bit)
        {
            base.WriteHexEncoded(buffer);
            SetBit(bit);
        }
        /// <summary>
        /// Write byte
        /// </summary>
        /// <param name="value">Value</param>
        /// <param name="bit">Field number</param>
        public void WriteByte(byte value, int bit)
        {
            base.WriteByte(value);
            SetBit(bit);
        }

        #endregion

        /// <summary>
        /// Basic constructor
        /// </summary>
        public MemoryStreamISO8583Ex() : base() { }

    }

    /// <summary>
    /// Memory stream extended
    /// </summary>
    public class MemoryStreamEx : MemoryStream
    {

        #region Reader
        /// <summary>
        /// Skips number of bytes in a given memory stream
        /// </summary>
        /// <param name="count">Number of bytes to skipp</param>
        public long Skip(int count)
        {
			
            long ps = Position;
            return Seek(count, SeekOrigin.Current)-ps;
        }

        /// <summary>
        /// Returns the index of next occurence of byte b in the stream starting at current position
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public long FindNext(byte b)
        {
            byte[] ba = GetBuffer();
            return Array.IndexOf<byte>(ba, b, (int)Position);
        }

        /// <summary>
        /// Skips the stream until the given byte is found. If byte is not found skips to the end of stream
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public long SkipTo(byte b)
        {
            long rez = FindNext(b);
            return rez == -1 ? Seek(0, SeekOrigin.End) : Seek(rez, SeekOrigin.Begin);
        }

        /// <summary>
        /// Reads the stream until given byte is found. If byte is not found reads to the end of stream
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public byte[] ReadTo(byte b)
        {
            long rez = FindNext(b);
            byte[] ba = new byte[rez - Position];
            Read(ba);
            return ba;
        }

        public string ReadStringTo(byte b, Encoding encoding)
        {
            if (encoding == null)
                throw new ArgumentNullException("Encoding cannot be null");
            return encoding.GetString(ReadTo(b));
        }

        /// <summary>
        /// Reads number of bytes in a given memory stream
        /// </summary>
        /// <param name="count">Number of bytes to read</param>
        /// <returns>Bytes read</returns>
        public byte[] Read(int count)
        {
            byte[] ba = new byte[count];
            int i = Read(ba, 0, count);
            if (i != count) 
            {
                byte[] bb = new byte[i];
                Buffer.BlockCopy(ba, 0, bb, 0, i);
                return bb;
            }
            return ba;
        }

        /// <summary>
        /// Reads HEX encoded byte array.
        /// </summary>
        /// <param name="count">Number of HEX encoded bytes to read. (1 HEX encoded byte will read out 2 bytes from memory stream)</param>
        /// <returns>Byte array</returns>

        public byte[] ReadHexEncoded(int count)
        {
            return Misc.DecodeHex(ReadString(count << 1));
        }

        /// <summary>
        /// Reads bytes from memory stream to given buffer up to the length of buffer
        /// </summary>
        /// <param name="buffer">Buffer to be filled with bytes from memory stream</param>
        /// <returns>Number of bytes readed</returns>
        public int Read(byte[] buffer)
        {
            return Read(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// Reads string (ASCII) from memory stream
        /// </summary>
        /// <param name="count">Number of bytes to read</param>
        /// <returns>ASCII string</returns>
        public string ReadString(int count)
        {
            byte[] ba = new byte[count];
            int i = Read(ba, 0, ba.Length);
            return Encoding.ASCII.GetString(ba, 0, i);
        }
        private static char[] ca = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

        /// <summary>
        /// Reads BCD encoded string.
        /// </summary>
        /// <param name="count">Number of nibles to read. (2 nibbles = 1 byte in memory stream, 1 nibble = 1 char in string)</param>
        /// <returns>ASCII string</returns>
        public string ReadBcdString(int count)
        {
            byte[] ba = new byte[(count + (count & 1)) >> 1];
            int i = Read(ba, 0, ba.Length);
            char[] cc = new char[i << 1];
            for (int ck = 0; ck < i; ck ++)
            {
                cc[(ck << 1)]	= ca[(ba[ck] >> 4)];
                cc[(ck << 1)+1]	= ca[(ba[ck] & 0x0F)];
            }
            return new string(cc, count & 1, count);
        }
        /// <summary>
        /// Reads HEX encoded byte array.
        /// </summary>
        /// <param name="count">Number of HEX encoded bytes to read. (1 HEX encoded byte will read out 2 bytes from memory stream)</param>
        /// <returns>Byte array</returns>
        public byte[] ReadHextEncoded(int count)
        {
            return Misc.DecodeHex(ReadString(count << 1));
        }
        #endregion

        #region Writing

        /// <summary>
        /// Fills memory stream with given value
        /// </summary>
        /// <param name="ch">Char to fill</param>
        /// <param name="count">Count of charachters to fill</param>
        public void Fill(char ch, int count)
        {
            byte[] bb = new byte[count];
            for (int i = 0; i < count; i++) 	bb[i] = (byte)ch;
            Write(bb);
        }
        /// <summary>
        /// Fills memory stream with given value
        /// </summary>
        /// <param name="b">Byte to fill</param>
        /// <param name="count">Count of bytes to fill</param>
        public void Fill(byte b, int count)
        {
            byte[] bb = new byte[count];
            for (int i = 0; i < count; i++) 	bb[i] = b;
            Write(bb);
        }

        /// <summary>
        /// Writes whole given buffer to memory stream
        /// </summary>
        /// <param name="buffer">Buffer to write</param>
        public void Write(byte[] buffer)
        {
            base.Write(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// Performs BCD encoding on a string and writes results to memory steam. Bcd encoding is case insensitive
        /// </summary>
        /// <param name="s">String to BCD encode and write</param>
        public void WriteBcdEncoded(string s)
        {
            int ps = s.Length & 1;
            byte b = 0;
            for (int ck = 0; ck < s.Length; ck ++)
            {
                switch (char.ToUpper(s[ck]))
                {
                    default : throw new Exception("Invalid character for BCD encoding");
                    case '0': if (ps == 0) b = 0;	else WriteByte(b); break;
                    case '1': if (ps == 0) b = 16;	else WriteByte((byte)(b | 1)); break;
                    case '2': if (ps == 0) b = 32;	else WriteByte((byte)(b | 2)); break;
                    case '3': if (ps == 0) b = 48;	else WriteByte((byte)(b | 3)); break;
                    case '4': if (ps == 0) b = 64;	else WriteByte((byte)(b | 4)); break;
                    case '5': if (ps == 0) b = 80;	else WriteByte((byte)(b | 5)); break;
                    case '6': if (ps == 0) b = 96;	else WriteByte((byte)(b | 6)); break;
                    case '7': if (ps == 0) b = 112;	else WriteByte((byte)(b | 7)); break;
                    case '8': if (ps == 0) b = 128;	else WriteByte((byte)(b | 8)); break;
                    case '9': if (ps == 0) b = 144;	else WriteByte((byte)(b | 9)); break;
                    case 'A': if (ps == 0) b = 160;	else WriteByte((byte)(b | 10)); break;
                    case 'B': if (ps == 0) b = 176;	else WriteByte((byte)(b | 11)); break;
                    case 'C': if (ps == 0) b = 192;	else WriteByte((byte)(b | 12)); break;
                    case 'D':
                    case '=': if (ps == 0) b = 208;	else WriteByte((byte)(b | 13)); break;
                    case 'E': if (ps == 0) b = 224;	else WriteByte((byte)(b | 14)); break;
                    case 'F': if (ps == 0) b = 240;	else WriteByte((byte)(b | 15)); break;
                }
                ps ^= 1;
            }
        }

        /// <summary>
        /// Writes ASCII string to memory stream
        /// </summary>
        /// <param name="s">Value</param>
        public void WriteString(string s)
        {
            if (s == null || s.Length == 0)
                return;
            Write(Encoding.ASCII.GetBytes(s));
        }

        private static char[] HX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        /// <summary>
        /// Hex encodes byte array and writes result to memory stream
        /// </summary>
        /// <param name="buffer">Byte array to hex encode and write to memory stream</param>
        public void WriteHexEncoded(byte[] buffer)
        {
            if (buffer == null || buffer.Length == 0) return;
            byte[] ca = new byte[buffer.Length << 1];
            for (int ck = 0; ck < buffer.Length; ck ++)
            {
                byte b = buffer[ck];
                ca[ck << 1]		= (byte)HX[b >> 4];
                ca[(ck << 1)+1]	= (byte)HX[b & 0x0F];
            }
            Write(ca, 0, ca.Length);
        }

        /// <summary>
        /// Write Int32
        /// </summary>
        /// <param name="i">Value</param>
        public void WriteInt32(Int32 i)
        {
            Write(BitConverter.GetBytes(i), 0, 4);
        }
        /// <summary>
        /// Write Int16
        /// </summary>
        /// <param name="i">Value</param>
        public void WriteInt16(Int16 i)
        {
            Write(BitConverter.GetBytes(i), 0, 2);
        }

        /// <summary>
        /// Writes UInt32
        /// </summary>
        /// <param name="i">Value</param>
        public void WriteUInt32( UInt32 i )
        {
            WriteUInt32(i, false);
        }

        public void WriteUInt32( UInt32 i, bool usetNetworkByteOrder )
        {
            if( usetNetworkByteOrder )
            {
                byte[] ba = BitConverter.GetBytes(i);
                Log4cs.Log(Importance.Warning, "Using network byte order!!!");
                Array.Reverse(ba);
                Write(ba, 0, 4);
            } else
            {
                Write(BitConverter.GetBytes(i), 0, 4);
            }
        }
        #endregion

        /// <summary>
        /// Create memory stream
        /// </summary>
        /// <param name="b">Buffer containing initial data</param>
        /// <param name="start">Offset</param>
        /// <param name="length">Count</param>
        public MemoryStreamEx(byte[] b, int start, int length) : base(b, start, length) { }
        /// <summary>
        /// Create memory stream
        /// </summary>
        /// <param name="b">Buffer containing initial data</param>
        public MemoryStreamEx(byte[] b) : base(b) { }
        /// <summary>
        /// Create memory stream
        /// </summary>
        public MemoryStreamEx() : base() { }

        public static MemoryStreamEx operator ++(MemoryStreamEx ms)
        {
            ms.Seek(1, SeekOrigin.Current);
            return ms;
        }
    }
    #endregion

    #region Misc

    /// <summary>
    /// Provides mis static methods
    /// </summary>
    public sealed class Misc
    {
        #region Hex Encode/Decode
        private static char[] HX = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

        /// <summary>
        /// Encode hex
        /// </summary>
        /// <param name="ba">ByteArray to encode</param>
        /// <returns>Hex encoded data</returns>
        public static string EncodeHex( byte[] ba )
        {
            if( ba == null ) return null;
            char[] ca = new char[ba.Length << 1];
            for( int ck = 0; ck < ba.Length; ck++ )
            {
                byte b = ba[ck];
                ca[(ck << 1)] = HX[b >> 4];
                ca[(ck << 1) + 1] = HX[b & 0x0F];
            }
            return new string(ca);
        }

        public static string EncodeHex( byte[] ba, int offset, int length )
        {
            if (ba == null) return null;
            char[] ca = new char[length << 1];
            //char[] ca = new char[ba.Length << 1];
            //for (int ck = offset; ck < offset + length; ck++)
            for( int ck = 0; ck < length; ck++ )
            {
                byte b = ba[ck + offset];
                ca[(ck << 1)] = HX[b >> 4];
                ca[(ck << 1) + 1] = HX[b & 0x0F];
            }
            return new string(ca);
        }
        /// <summary>
        /// Decode hex
        /// </summary>
        /// <param name="s">Hex encoded data</param>
        /// <returns>Decoded ByteArray</returns>
        public static byte[] DecodeHex(string s)
        {
            if (s == null) return null;
            byte[] ba = new byte[s.Length >> 1];
            int ck = 0;
            for (int i = 0; i < ba.Length; i++)
            {
                switch (s[ck++])
                {
                    case '0': break;
                    case '1': ba[i] = 0x10; break;
                    case '2': ba[i] = 0x20; break;
                    case '3': ba[i] = 0x30; break;
                    case '4': ba[i] = 0x40; break;
                    case '5': ba[i] = 0x50; break;
                    case '6': ba[i] = 0x60; break;
                    case '7': ba[i] = 0x70; break;
                    case '8': ba[i] = 0x80; break;
                    case '9': ba[i] = 0x90; break;
                    case 'A': 
                    case 'a': ba[i] = 0xA0; break;
                    case 'B': 
                    case 'b': ba[i] = 0xB0; break;
                    case 'C': 
                    case 'c': ba[i] = 0xC0; break;
                    case 'D': 
                    case 'd': ba[i] = 0xD0; break;
                    case 'E': 
                    case 'e': ba[i] = 0xE0; break;
                    case 'F': 
                    case 'f': ba[i] = 0xF0; break;
                    default: throw new ArgumentException("String is not hex encoded data.");
                }
                switch (s[ck++])
                {
                    case '0': break;
                    case '1': ba[i] |= 0x01; break;
                    case '2': ba[i] |= 0x02; break;
                    case '3': ba[i] |= 0x03; break;
                    case '4': ba[i] |= 0x04; break;
                    case '5': ba[i] |= 0x05; break;
                    case '6': ba[i] |= 0x06; break;
                    case '7': ba[i] |= 0x07; break;
                    case '8': ba[i] |= 0x08; break;
                    case '9': ba[i] |= 0x09; break;
                    case 'A': 
                    case 'a': ba[i] |= 0x0A; break;
                    case 'B': 
                    case 'b': ba[i] |= 0x0B; break;
                    case 'C': 
                    case 'c': ba[i] |= 0x0C; break;
                    case 'D': 
                    case 'd': ba[i] |= 0x0D; break;
                    case 'E': 
                    case 'e': ba[i] |= 0x0E; break;
                    case 'F': 
                    case 'f': ba[i] |= 0x0F; break;
                    default: throw new ArgumentException("String is not hex encoded data.");
                }
            }
            return ba;
        }
        #endregion

        #region Padding right/left
        /// <summary>
        /// Pad Right (left aligns data)
        /// </summary>
        /// <param name="val">Value</param>
        /// <param name="Length">Length</param>
        /// <param name="spc">FillChar</param>
        /// <returns>Padded or Cut string</returns>
        public static string PadRight(string val, int Length, char spc)
        {
            if (val == null) return "".PadLeft(Length, spc);
            return val.Length > Length ? val.Substring(val.Length - Length, Length) : val.PadRight(Length, spc);
        }
        /// <summary>
        /// Pad Left (right aligns data)
        /// </summary>
        /// <param name="val">Value</param>
        /// <param name="Length">Length</param>
        /// <param name="spc">FillChar</param>
        /// <returns>Padded or Cut string</returns>
        public static string PadLeft(string val, int Length, char spc)
        {
            if (val == null) return "".PadLeft(Length, spc);
            return val.Length > Length ? val.Substring(0, Length) : val.PadLeft(Length, spc);
        }
        #endregion

        #region MD5
        private static System.Security.Cryptography.MD5 md5a = new MD5CryptoServiceProvider();
        private static System.Security.Cryptography.MD5 md5b = new MD5CryptoServiceProvider();
        private static System.Security.Cryptography.MD5 md5c = new MD5CryptoServiceProvider();
        /// <summary>
        /// Hash Guid using MD5 algorithm
        /// </summary>
        /// <param name="s">Value</param>
        /// <returns>Hashed Guid</returns>
        public static Guid HashGuid(string s)
        {
            if (s == null) return Guid.Empty;
            lock(md5a)
            {
                return new Guid (md5a.ComputeHash(Encoding.UTF8.GetBytes(s)));
            }
        }
        /// <summary>
        /// Hash Guid using MD5 algorithm
        /// </summary>
        /// <param name="ba">Value</param>
        /// <returns>Hashed Guid</returns>
        public static Guid HashGuid(byte[] ba)
        {
            lock(md5b)
            {
                return new Guid (md5b.ComputeHash(ba));
            }
        }
        /// <summary>
        /// Calculate MD5
        /// </summary>
        /// <param name="ba">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="count">Count</param>
        /// <returns>Hash</returns>
        public static byte[] MD5(byte[] ba, int offset, int count)
        {
            lock(md5c)
            {
                return md5c.ComputeHash(ba, offset, count);
            }
        }
        #endregion

        #region Magstripe and chip card field utilities
        public static bool IsChipCardTrack2(string track2)
        {
            if (track2 == null || track2.Length == 0)
                return false;
            int idx = track2.IndexOfAny(new char[] { '=', 'D' }) + 5; //Skip delimeter and expiration date
            if (idx >= track2.Length)
                return false;
            try
            {
//                Phoenix.TraceLog("Checking card service code for chip ({0})", track2.Substring(idx, 3));
                uint data_code = uint.Parse(track2[idx].ToString(), System.Globalization.NumberStyles.AllowHexSpecifier);
                return (data_code & 2) == 2;
            }
            catch
            {
                return false;
            }
        }

        public static string GetCardServiceCode(string track2)
        {
            if (track2 == null || track2.Length == 0)
                return null;
            int idx = track2.IndexOfAny(new char[] { '=', 'D' }) + 5; //Skip delimeter and expiration date
            if (idx >= track2.Length)
                return null;
            try
            {
                return track2.Substring(idx, 3);
            }
            catch
            {
                return null;
            }
        }

        public static string ExtractCardPAN(string track2, string mask)
        {
            //Log4cs.Log(Importance.Debug, "Track2 to extract PAN is: " + track2);
            if (track2 == null)
                throw new Exception("Track2 is empty");
            int pos = track2.IndexOfAny(new char[] { '=', 'D', 'd' });
            if (pos == -1)
                throw new Exception("Invalid track2");
                //return track2.Length > 16 ? track2.Substring(1, 16) : track2;
            string s = track2.Substring(0, pos).TrimStart(new char[] { ';' });
            if (mask != null)
            {
                //Apply a mask
                if (mask.Length != 19)
                    mask = Misc.PadLeft(mask, 19, '*');
                StringBuilder sb = new StringBuilder();
                for (int i = 19 - s.Length; i < 19; i++)
                {
                    sb.Append(mask[i] == '*' ? '*' : s[i - (19 - s.Length)]);
                }
                s = sb.ToString();
            }
            return s;
        }

        public static string MaskPan( string s, string mask )
        {
            //Log4cs.Log(Importance.Debug, "Masking {0} using mask {1}", s, mask);
            if( mask != null )
            {
                //Apply a mask
                if( mask.Length != 19 )
                    mask = Misc.PadLeft(mask, 19, '*');
                StringBuilder sb = new StringBuilder();
                for( int i = 19 - s.Length; i < 19; i++ )
                {
                    sb.Append(mask[i] == '*' ? '*' : s[i - (19 - s.Length)]);
                }
                s = sb.ToString();
            }

            return s;
        }

        public static string ExtractCardExp(string track2)
        {
            if (track2 == null)
                return null;
            int pos = track2.IndexOfAny(new char[] { '=', 'D' });
            if (pos == -1)
                return null;
            string s = track2.Substring(pos + 1, 4);
            if (s.Length != 4)
                return null;
            return s.Substring(2, 2) + "/" + s.Substring(0, 2);
        }

        public static bool IsCardEffective(string track2)
        {
            if (track2 == null)
                return false;
            int pos = track2.IndexOfAny(new char[] { '=', 'D' });
            if (pos == -1)
                return false;
            string s = track2.Substring(pos + 1, 4);
            if (s.Length != 4)
                return false;
            int cardexp = int.Parse("20" + s);
            int now = DateTime.Now.Year * 100 + DateTime.Now.Month;
            return now <= cardexp;
        }

        #endregion

        /// <summary>
        /// Makes sum in major currency out of the minor
        /// </summary>
        /// <param name="sum"></param>
        /// <returns></returns>
        public static string MakeMajorCurrency(string sum)
        {
            return sum.Length < 3 ? "0." + PadLeft(sum, 2, '0') : sum.Insert(sum.Length - 2, ".");
        }

        /// <summary>
        /// Takes sum w/ dot or comma and returns it in minor
        /// </summary>
        public static int MakeMinorCurrency(string sum)
        {
            int n = 0;

            try
            {
                string[] ar = sum.Split(new char[] { ',', '.' });
                n = Int32.Parse(ar[0]) * 100;
                n += Int32.Parse(ar[1]);

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Can't convert to minor!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                n = -1;
            }
            return n;
        }

        /// <summary>
        /// Gets path of executable and ensures its ends w/ '\'
        /// </summary>
        public static string GetPath()
        {
            string aPath = "";
            try
            {
                System.Reflection.Module[] modules = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
                aPath = System.IO.Path.GetDirectoryName(modules[0].FullyQualifiedName);
                if( (aPath != "") && (aPath[aPath.Length - 1] != '\\') ) aPath += '\\';
            } catch( Exception ){}

            return aPath;
        }

        /// <summary>
        /// Network byte order - most significant byte is first. 256 will be 01 00
        /// </summary>
        public static void NumberToBuffer( ref byte[] buf, int offset, int lengthToWrite, bool networkByteOrder )
        {
            byte low = (byte)(lengthToWrite & 0xFF);
            byte high = (byte)((lengthToWrite & 0xFF00) >> 8);

            //Log4cs.Log(Importance.Debug, "Using {0}network byte order", (networkByteOrder) ? "" : "non-");
            //Log4cs.Log("Length {0}: low {1} and high {2}", lengthToWrite, low, high);

            if( networkByteOrder )
            {
                buf[offset] = high;
                buf[offset + 1] = low;
            } else
            {
                buf[offset] = low;
                buf[offset + 1] = high;
            }
        }

        public static void NumberToBuffer( ref byte[] buf, int offset, UInt32 lengthToWrite, bool networkByteOrder, int bytesToFill )
        {
            try
            {
                byte[] b = new byte[4];
                b[0] = (byte)((lengthToWrite & 0xFF000000) >> 24);
                b[1] = (byte)((lengthToWrite & 0x00FF0000) >> 16);
                b[2] = (byte)((lengthToWrite & 0x0000FF00) >> 8);
                b[3] = (byte)(lengthToWrite & 0x000000FF);

                //Log4cs.Log(Importance.Debug, "{0} as bytes: {1}", lengthToWrite, Misc.EncodeHex(b));

                // Most significant byte is first
                if( networkByteOrder )
                {
                    for( int i = 1; i <= bytesToFill; i++ )
                    {
                        //Log4cs.Log(Importance.Debug, "buf[{0} + {1} - {2}] = b[3 - {2}]", offset, bytesToFill, i);
                        buf[offset + bytesToFill - i] = b[4 - i];
                    }
                } else
                {
                    for( int i = 0; i < bytesToFill; i++ )
                        buf[offset + i] = b[3 - i];
                }

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error splitting {0} to buffer!", lengthToWrite);
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        //public static UInt32 BuffertToUint32( byte[] buf, int offset, bool networkByteOrder, int bytesQnt )
        //{
        //    //byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
        //    UInt32 len = 0;

        //    if( bytesQnt > 4 )
        //        throw new ArgumentException("Number of bytes is to big!");

        //    if( (buf == null) || (buf.Length < bytesQnt) )
        //        throw new ArgumentOutOfRangeException("Buffer is null or too small");

        //    //Log4cs.Log(Importance.Debug, "Length bytes: {0} {1}. Using {2}network byte order", buf[offset], buf[offset + 1], (networkByteOrder) ? "" : "non-");
        //    // Most significant byte is first
        //    int shift = 0;
        //    if( networkByteOrder )
        //    {
        //        shift = (bytesQnt - 1) * 8;
        //        for( int i = 0; i < bytesQnt; i++ )
        //        {
        //            len += (UInt32)(buf[offset + i] << shift);
        //            shift -= 8;
        //        }
        //    } else
        //    {
        //        len += buf[offset];
        //        len += (UInt32)(buf[offset + 1] << 8);
        //    }

        //    return len;
        //}

        /// <summary>
        /// Network byte order - most significant byte is first. 256 will be 01 00
        /// </summary>
        public static int BufferToNumber( byte[] buf, int offset, bool networkByteOrder, int bytesQnt )
        {
            //byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
            int len = 0;

            if( bytesQnt > 4 )
                throw new ArgumentException("Number of bytes is to big!");

            //Log4cs.Log(Importance.Debug, "Length bytes: {0} {1}. Using {2}network byte order", buf[offset], buf[offset + 1], (networkByteOrder) ? "" : "non-");
            // Most significant byte is first
            int shift = 0;
            if( networkByteOrder )
            {
                shift = (bytesQnt-1) * 8;
                for( int i = 0; i < bytesQnt; i++ )
                {
                    //Log4cs.Log(Importance.Debug, "buf<{0} + {1}> << {2}", offset, i, shift);
                    len += buf[offset + i] << shift;
                    shift -= 8;
                }
                //len += buf[offset + 1];
                //len += (buf[offset] << 8);
            } else
            {
                len += buf[offset];
                len += (buf[offset + 1] << 8);
            }

            return len;
        }

        /// <summary>
        /// Network byte order - most significant byte is first. 256 will be 01 00
        /// </summary>
        public static UInt32 BufferToUnsignedNumber( byte[] buf, int offset, bool networkByteOrder, int bytesQnt )
        {
            //byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
            UInt32 len = 0;

            if( bytesQnt > 4 )
                throw new ArgumentException("Number of bytes is to big!");

            //Log4cs.Log(Importance.Debug, "Length bytes: {0} {1}. Using {2}network byte order", buf[offset], buf[offset + 1], (networkByteOrder) ? "" : "non-");
            // Most significant byte is first
            int shift = 0;
            if( networkByteOrder )
            {
                shift = (bytesQnt - 1) * 8;
                for( int i = 0; i < bytesQnt; i++ )
                {
                    //Log4cs.Log(Importance.Debug, "buf<{0} + {1}> << {2}", offset, i, shift);
                    len += (UInt32)(buf[offset + i] << shift);
                    shift -= 8;
                }
                //len += buf[offset + 1];
                //len += (buf[offset] << 8);
            } else
            {
                len += buf[offset];
                len += (UInt32)(buf[offset + 1] << 8);
            }

            return len;
        }


        /// <summary>
        /// Waits for any specified event
        /// </summary>
        /// <param name="arEvents">Events that could be</param>
        /// <param name="myEvents">One of events to wait</param>
        /// <param name="timeoutMs">Timeout</param>
        /// <returns>Id of event or WaitTimeout</returns>
        public static int WaitMyEvents( ref ManualResetEvent[] arEvents, int[] arEventsToWait, int timeoutMs, bool resetEvent )
        {
            int nEventId = WaitHandle.WaitTimeout;
            // How long we are waiting
            int waitingTimeMs = 0;
            DateTime start;

            while( waitingTimeMs < timeoutMs )
            {
                start = DateTime.Now;
                //Log4cs.Log("Waiting for " + (timeoutMs - waitingTimeMs) + " ms.");
                nEventId = WaitHandle.WaitAny(arEvents, timeoutMs - waitingTimeMs, false);
                if( nEventId != WaitHandle.WaitTimeout )
                {
                    // Check if we receive desired event
                    if( Array.IndexOf(arEventsToWait, nEventId) >= 0 )
                    {

                        if( resetEvent )
                        {
                            Log4cs.Log(Importance.Debug, "Reseting event with ID: {0}", nEventId);
                            arEvents[nEventId].Reset();
                        }
                        return nEventId;
                    } else
                    {
                        waitingTimeMs += ((TimeSpan)(DateTime.Now - start)).Milliseconds;
                    }
                } else
                {
                    // Stop waiting and return
                    waitingTimeMs = timeoutMs;
                }
            }  // END WHILE

            return nEventId;
        }

        //public static string[] DecryptToString( string filename, string pass, Encoding enc )
        //{
        //    string[] arText = null;
        //    //try
        //    //{
        //    //    byte[] buf = File.ReadAllBytes(filename);
        //    //    BlowfishSimple bf = new BlowfishSimple(pass);
        //    //    string sText = bf.Decrypt(Encoding.ASCII.GetString(buf));
        //    //    arText = sText.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
        //    //} catch( Exception FileNotFoundException )
        //    //{
        //    //    Log4cs.Log(Importance.Error, "File '{0}' not found!", filename);
        //    //}

        //    return arText;
        //}


    }
    #endregion

    #region BitMap
    /// <summary>
    /// ISO8583 Bitmap
    /// </summary>
    public sealed class BitMap
    {
        private byte[] bb;
        private BitMap() {}

        /// <summary>
        /// Get bit value
        /// </summary>
        public bool this [int bit]
        {
            get
            {
                bit--;
                int ps = (bit / 8);
                byte bt = (byte)(128 >> (bit % 8));
                return (bb[ps] & bt) == bt;
            }
        }
        /// <summary>
        /// Create bitmap from ByteArray
        /// </summary>
        /// <param name="bitmap">Buffer</param>
        public BitMap(byte[] bitmap)
        {
            bb = bitmap;
        }

        //		public void Set(int bit, bool Value)
        //		{
        //			bit--;
        //			int ps = (bit / 8);
        //			byte bt = (byte)(128 >> (bit % 8));
        //			bb[ps] = Value ? (byte)(bb[ps] | bt) : (byte)(bb[ps] & (bt ^ 0xFF));
        //		}
			

        //		public byte[] GetBytes()
        //		{
        //			return bb;
        //		}
    }
    #endregion

    #region CRC16
    //TODO: add summary comments on CRC16

    /// <summary>
    /// 
    /// </summary>
    public class CRC16
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="c"></param>
        /// <param name="crc"></param>
        /// <returns></returns>
        protected static ushort NextCRC_MSB(byte c, ushort crc)
        {
            byte t = (byte)(c ^ (crc >> 8));
            t = (byte)(t ^ (t >> 4));
            t = (byte)(t ^ (t >> 2));
            t = (byte)(t ^ (t >> 1));
            return (ushort)((crc << 8) ^ (t << 15) ^ (t << 2) ^ t);
        }
				
        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static ushort Calc(byte[] source, long offset, long count)
        {
            ushort rez = 0x0000;
            long x = offset + count;
            for (long i = offset; i < x; i++)
            {
                rez = NextCRC_MSB(source[i], rez);
            }
            return rez;
        }
    }

    #endregion

    #region CRC32
    /// <summary>
    /// CRC32 Calculation
    /// </summary>
    public class CRC32
    {
        private CRC32(){}	
        private static readonly uint[] table = new uint[] {
                                                              0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
                                                              0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
                                                              0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
                                                              0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
                                                              0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
                                                              0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
                                                              0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
                                                              0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
                                                              0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
                                                              0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
                                                              0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
                                                              0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
                                                              0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
                                                              0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
                                                              0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
                                                              0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
                                                              0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
                                                              0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
                                                              0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
                                                              0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
                                                              0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
                                                              0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
                                                              0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
                                                              0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
                                                              0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
                                                              0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
                                                              0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
                                                              0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
                                                              0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
                                                              0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
                                                              0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
                                                              0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
                                                              0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
                                                              0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
                                                              0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
                                                              0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
                                                              0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
                                                              0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
                                                              0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
                                                              0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
                                                              0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
                                                              0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
                                                              0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
                                                              0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
                                                              0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
                                                              0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
                                                              0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
                                                              0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
                                                              0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
                                                              0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
                                                              0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
                                                              0x2d02ef8d
                                                          };

        /// <summary>
        /// Calculate CRC32
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <returns>CRC32</returns>
        public static uint Calc(byte[] buffer)
        {
            return Calc(buffer, 0, buffer.Length);
        }
        /// <summary>
        /// Calculate CRC32
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="length">Count</param>
        /// <returns>CRC32</returns>
        public static uint Calc(byte[] buffer, int offset, int length)
        {
            uint val = 0;
            for (int i = offset; i < offset + length; i ++)
                val = table[(int)((val ^ buffer[i]) & 0xff)] ^ (val >> 8);
            return val;
        }

    }	
    #endregion

    #region Terner's Search Tree

    public class TSTree : IEnumerable
    {
        internal class DE
        {
            internal char splitChar;
            internal bool isKey = false;
            internal string Key = null;
            internal object Value = null;
            internal DE parent;
            internal DE lowChild = null;
            internal DE eqChild = null;
            internal DE highChild = null;
            public DE(DE parent, char splitChar)
            {
                this.parent = parent;
                this.splitChar = splitChar;
            }
        }
        private DE root;

        #region Constructors
        /// <summary>
        /// Create new code list with default values
        /// </summary>
        public TSTree()
        {
            root=null;
        }
        #endregion

        #region Get/Set members
        public void Set(object obj)
        {
            if (obj == null)
                return;
            string Key = obj.ToString();
            if (Key.Length == 0) return;
            if (root == null)	root = new DE(null,Key[0]);
            DE p = root;
            int i = 0;
            char c;
            while (i<Key.Length)
            {
                c = Key[i];
                if (c < p.splitChar)		p = (p.lowChild==null)	? (p.lowChild	= new DE(p,c)) :p.lowChild;
                else if (c > p.splitChar)	p = (p.highChild==null)	? (p.highChild	= new DE(p,c)) : p.highChild;
                else if (++i < Key.Length)	p = (p.eqChild==null)	? (p.eqChild	= new DE(p,Key[i])) : p.eqChild;
            }
            p.isKey = true;
            p.Key = Key;
            p.Value = obj;
        }
	

        /// <summary>
        /// Clear all codes
        /// </summary>
        public void Clear()
        {
            root = null;
        }
    
        /// <summary>
        /// Get text value if particular language value is not found then default code value is returned.
        /// </summary>
        /// <param name="Key">Key</param>
        /// <returns>Object</returns>
        public object Get(string Key)
        {
            if (Key == null || Key.Length == 0) return null;
            string s = Key;
            int n = s.Length;
            DE p = root;	
            int i=0;
            char c;
            while(i<n && p!=null)
            {
                c = s[i];
                if (c < p.splitChar)		p=p.lowChild;
                else if (c > p.splitChar)	p=p.highChild;
                else
                {
                    ++i;
                    if (p.isKey && i == s.Length) 
                    {
                        return p.Value;
                    }
                    p=p.eqChild;
                }		
            }
            return null;
        }

        /// <summary>
        /// Get the value of the longest found key.
        /// </summary>
        /// <param name="Key">Key</param>
        /// <returns>Object</returns>
        public object PartialGet(string Key)
        {
            if (Key == null || Key.Length == 0) return null;
            string s = Key;
            object rez = null;
            int n = s.Length;
            DE p = root;
            int i = 0;
            char c;
            while (i < n && p != null)
            {
                c = s[i];
                if (c < p.splitChar) p = p.lowChild;
                else if (c > p.splitChar) p = p.highChild;
                else
                {
                    ++i;
                    if (p.isKey)
                    {
                        rez = p.Value;
                    }
                    p = p.eqChild;
                }
            }
            return rez;
        }
        #endregion

        #region IEnumerable Members

        private sealed class Enumerator : IEnumerator
        {
            private DE root;
            private Stack stack = new Stack();
            private DE p = null;
			
            internal Enumerator(DE root)
            {
                this.root = root;
                if (root != null)	stack.Push(root);
            }

            #region IEnumerator Members

            public void Reset()
            {
                stack.Clear();
                if (root != null)	stack.Push(root);
                p = null;
            }

            public object Current
            {
                get
                {
                    return p.Value;
                }
            }

            public bool MoveNext()
            {
                while(stack.Count > 0)
                {
                    p = (DE)stack.Pop();
                    if (p.highChild != null) stack.Push(p.highChild);
                    if (p.eqChild != null) stack.Push(p.eqChild);
                    if (p.lowChild != null) stack.Push(p.lowChild);
                    if (p.isKey) return true;
                }
                return false;
            }

            #endregion

        }


        /// <summary>
        /// </summary>
        /// <returns>SDO encoded data enumerator</returns>
        public IEnumerator GetEnumerator()
        {
            return new Enumerator(root);
        }

        #endregion
    }
    #endregion

    #region Advanced TCP socket
    /// <summary>
    /// TcpSocket
    /// </summary>
    public class TcpSocket : IDisposable
    {
        private Int64 _act = DateTime.Now.Ticks;
        /// <summary>
        /// Socket idle time in seconds
        /// </summary>
        public Int32 Idle
        {	get { return Connected ? (Int32)((DateTime.Now.Ticks - _act) / TimeSpan.TicksPerMillisecond) : 0; }}
        /// <summary>
        /// Tag
        /// </summary>
        public object tag = null;
        /// <summary>
        /// Error text
        /// </summary>
        public string ErrorTxt = "";

        /// <summary>
        /// True if socket is connected
        /// </summary>
        public bool  Connected
        {
            get
            {
                if (_Socket != null)
                    if (_Socket.Poll(0, SelectMode.SelectRead) && _Socket.Available == 0)
                        CloseSocket();
                return _Socket == null ? false : _Socket.Connected;
            }
        }

        /// <summary>
        /// Underlaying socket
        /// </summary>
        public Socket socket
        {
            get 
            {
                return _Socket;
            }
        }
	
        private Socket _Socket = null;

        #region Constructors
        /// <summary>
        /// Create TcpSocket
        /// </summary>
        /// <param name="tmpSocket">Socket</param>
        /// <param name="Tag">Tag</param>
        public TcpSocket(Socket tmpSocket, object Tag)
        {
            tag = Tag;
            ErrorTxt = "";
            try 
            {
                _Socket = tmpSocket;
                _Socket.Blocking = true;
                //_Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 10000);
                //_Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, 0);
                //_Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, 2000);
                //_Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
            }
            catch (Exception e)
            {
                ErrorTxt = e.Message;
                _Socket = null;
            }
        }

        /// <summary>
        /// Port
        /// </summary>
        protected readonly int _port = -1;
        /// <summary>
        /// Address
        /// </summary>
        protected readonly string _address = null;
		
        /// <summary>
        /// Create TcpSocket
        /// </summary>
        /// <param name="Address">IP Address</param>
        /// <param name="Port">Port</param>
        /// <param name="Tag">Tag</param>
        public TcpSocket(IPAddress Address, int Port, object Tag)
        {
            _port = Port;
            _address = Address.ToString();
            if (_port >=0 && Address != null)
            {
                tag = Tag;
                ErrorTxt = "";
                try
                {
                    IPEndPoint lep = new IPEndPoint(Address, Port);
                    _Socket = new Socket(lep.Address.AddressFamily, SocketType.Stream,	ProtocolType.Tcp);
                    _Socket.Blocking = true;
                    /*
                    _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 10000);
                    _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, 0);
                    _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, 2000);
                    _Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);
                    */
                    _Socket.Connect(lep);
                }
                catch (Exception e)
                {
                    ErrorTxt = e.Message;
                    _Socket = null;
                }
            }
            else 
            {
                _Socket = null;
                ErrorTxt = "Invalid port number or network address";
            }
        }

        /// <summary>
        /// Create TcpSocket
        /// </summary>
        /// <param name="Address">Address</param>
        /// <param name="Port">Port</param>
        /// <param name="Tag">Tag</param>
        public TcpSocket(string Address, int Port, object Tag)
        {
            _port = Port;
            _address = Address;
            if (_port >=0 && Address != null)
            {
                tag = Tag;
                ErrorTxt = "";
                try
                {
                    //IPHostEntry lipa = Dns.Resolve(Address);
                    IPHostEntry lipa = Dns.GetHostEntry(Address);
                    IPEndPoint lep = new IPEndPoint(lipa.AddressList[0], Port);

                    _Socket = new Socket(lep.Address.AddressFamily, SocketType.Stream,	ProtocolType.Tcp);
                    _Socket.Blocking = true;
                    _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 10000);
                    _Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);

                    _Socket.Connect(lep);
                }
                catch (Exception e)
                {
                    ErrorTxt = e.Message;
                    _Socket = null;
                }
            }
            else 
            {
                _Socket = null;
                ErrorTxt = "Invalid port number or network address";
            }
        }
        public TcpSocket(IPEndPoint lep, object Tag)
        {
            _port = lep.Port;
            _address = lep.Address.ToString();
            if (_port >= 0 && _address != null)
            {
                tag = Tag;
                ErrorTxt = "";
                try
                {
                    _Socket = new Socket(lep.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    _Socket.Blocking = true;
                    _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 10000);
                    //_Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1);

                    _Socket.Connect(lep);
                }
                catch (Exception e)
                {
                    ErrorTxt = e.Message;
                    _Socket = null;
                }
            }
            else
            {
                _Socket = null;
                ErrorTxt = "Invalid port number or network address";
            }
        }
        #endregion


        /// <summary>
        /// Close socket
        /// </summary>
        public void CloseSocket()
        {
            try
            {
                if (_Socket != null) 
                {
                    try
                    {
                        _Socket.Shutdown(SocketShutdown.Both);
                    }
                    catch {}
                    try { _Socket.Close();}
                    catch {}
                    _Socket = null;
                }
            }
            catch {}
        }

        /// <summary>
        /// Amount of data available
        /// </summary>
        public int DataAvailable 
        {
            get 
            {
                return _Socket != null ? _Socket.Available : 0;
            }
        }


        /// <summary>
        /// Read all available bytes, non blocking call.
        /// </summary>
        /// <returns>Data readed</returns>
        //		public byte[] ReadByteArray()
        //		{
        //			if (_Socket != null && _Socket.Available > 0)
        //			{
        //				try 
        //				{
        //					byte[] ba = new byte[_Socket.Available];
        //					if (_Socket.Receive(ba, 0, ba.Length, SocketFlags.None) == ba.Length) return ba;
        //					else throw new Exception("[TCP] Read operation failure");
        //				}
        //				catch
        //				{
        //					CloseSocket();
        //					throw;
        //				}
        //			}
        //			return null;
        //		}


        /// <summary>
        /// Wait for any data and read all available data.
        /// </summary>
        /// <param name="TO">TimeOut</param>
        /// <returns>Data reader (null if no data readed or exception)</returns>
        public byte[] ReadByteArray(TimeOut TO)
        {
            if (_Socket == null) throw new Exception("[TCP] Socket disposed");
            try
            {
                if (_Socket.Poll(TO.Microseconds, SelectMode.SelectRead))
                {
                    byte[] ba = new byte[_Socket.Available];
                    if (ba.Length == 0) throw new Exception("[TCP] Socket closed on read operation");
                    if (_Socket.Receive(ba, 0, ba.Length, SocketFlags.None) == ba.Length) return ba;
                    else throw new Exception("[TCP] Read operation failure");
                }
                else throw new TimeoutException();
            }
            catch (TimeoutException)
            {
                if (TO.Fatal) CloseSocket();
                throw;
            }
            catch
            {
                CloseSocket();
                throw;
            }
        }


        /// <summary>
        /// Read number of bytes. Close socket if could not do it in a given amount of time.
        /// </summary>
        /// <param name="Count">Number of bytes readed</param>
        /// <param name="TO">Time out</param>
        /// <returns>Data readed</returns>
        public byte[] ReadByteArray(Int32 Count, TimeOut TO)
        {
            if (_Socket == null) throw new Exception("[TCP] Socket disposed");
            Int32 ps = 0;
            try
            {
                byte[] ba = new byte[Count];
                while (!TO)
                {
                    if (_Socket.Poll(TO.Microseconds, SelectMode.SelectRead))
                    {
                        _act = DateTime.Now.Ticks;
                        Int32 da = _Socket.Available;
                        if (da == 0) throw new Exception("[TCP] Socket closed on read operation");
                        ps += _Socket.Receive(ba, ps, Math.Min(da, Count - ps), SocketFlags.None);
                        if (ps == Count) return ba;
                    }
                    else throw new TimeoutException();
                }
            }
            catch (TimeoutException)
            {
                if (TO.Fatal || ps != 0) CloseSocket();
                throw;
            }
            catch
            {
                CloseSocket();
                throw;
            }
            throw new TimeoutException();
        }
        /// <summary>
        /// Skips socket stream data until target byte value is reached
        /// </summary>
        /// <param name="b">Target byte value</param>
        /// <param name="TO">Time out</param>
        public void SkipTo(byte b, TimeOut TO)
        {
            if (_Socket == null) throw new Exception("[TCP] Socket disposed");
            try
            {
                while (!TO)
                {
                    if (_Socket.Poll(TO.Microseconds, SelectMode.SelectRead))
                    {
                        _act = DateTime.Now.Ticks;
                        byte[] ba = new byte[_Socket.Available];
                        if (ba.Length == 0) throw new Exception("[TCP] Socket closed on skip operation");
                        _Socket.Receive(ba, 0, ba.Length, SocketFlags.Peek);
                        for (int i = 0; i < ba.Length; i++)
                            if (ba[i] == b)
                            {
                                if (_Socket.Receive(ba, 0, i + 1, SocketFlags.None) == i + 1) return;
                                else throw new Exception("[TCP] Could not skipt to byte value " + b);
                            }
                        _Socket.Receive(ba, 0, ba.Length, SocketFlags.None);
                    }
                    else throw new TimeoutException();
                }
            }
            catch (TimeoutException)
            {
                if (TO.Fatal) CloseSocket();
                throw;
            }
            catch
            {
                CloseSocket();
                throw;
            }
            throw new TimeoutException();
        }

        /// <summary>
        /// Reads socket stream data until target byte value is reached
        /// </summary>
        /// <param name="b">Target byte value</param>
        /// <param name="TO">Time out</param>
        public byte[] ReadTo(byte b, TimeOut TO)
        {
            if (_Socket == null) throw new Exception("[TCP] Socket disposed");
            MemoryStreamEx ms = new MemoryStreamEx();
            try
            {
                while (!TO)
                {
                    if (_Socket.Poll(TO.Microseconds, SelectMode.SelectRead))
                    {
                        _act = DateTime.Now.Ticks;
                        byte[] ba = new byte[_Socket.Available];
                        if (ba.Length == 0) throw new Exception("[TCP] Socket closed on read operation");
                        _Socket.Receive(ba, 0, ba.Length, SocketFlags.Peek);
                        for (int i = 0; i < ba.Length; i++)
                            if (ba[i] == b)
                            {
                                if (_Socket.Receive(ba, 0, i + 1, SocketFlags.None) == i + 1)
                                {
                                    ms.Write(ba, 0, i);
                                    return ms.ToArray();
                                }
                                else throw new Exception("[TCP] Could not read to byte value " + b);
                            }
                        if (_Socket.Receive(ba, 0, ba.Length, SocketFlags.None) != ba.Length)
                            throw new Exception("[TCP] Could not read to byte value " + b);
                        ms.Write(ba, 0, ba.Length);
                    }
                    else throw new TimeoutException();
                }
            }
            catch (TimeoutException)
            {
                if (TO.Fatal || ms.Length > 0) CloseSocket();
                throw;
            }
            catch
            {
                CloseSocket();
                throw;
            }
            throw new TimeoutException();
        }

        /// <summary>
        /// Waits for data and reads one byte
        /// </summary>
        /// <param name="TO">Time out</param>
        /// <returns>Byte readed</returns>
        public byte ReadByte(TimeOut TO)
        {
            if (_Socket == null) throw new Exception("[TCP] Socket disposed");
            try
            {
                if (_Socket.Poll(TO.Microseconds, SelectMode.SelectRead))
                {
                    if (_Socket.Available == 0)
                    {
                        CloseSocket();
                        throw new Exception("[TCP] Socket closed on read operation");
                    }
                    _act = DateTime.Now.Ticks;
                    byte[] myReadBuffer = new byte[1];
                    _Socket.Receive(myReadBuffer, 0, 1, SocketFlags.None);
                    return myReadBuffer[0];
                }
                else throw new TimeoutException();
            }
            catch (TimeoutException)
            {
                if (TO.Fatal) CloseSocket();
                throw;
            }
            catch
            {
                CloseSocket();
                throw;
            }
        }

        /// <summary>
        /// Waits for data and peeks one byte
        /// </summary>
        /// <param name="TO">Time out</param>
        /// <returns>Byte peeked</returns>
        public byte PeekByte(TimeOut TO)
        {
            if (_Socket == null) throw new Exception("[TCP] Socket disposed");
            try
            {
                if (_Socket.Poll(TO.Microseconds, SelectMode.SelectRead))
                {
                    if (_Socket.Available == 0)
                    {
                        CloseSocket();
                        throw new Exception("[TCP] Socket closed on read operation");
                    }
                    _act = DateTime.Now.Ticks;
                    byte[] myReadBuffer = new byte[1];
                    _Socket.Receive(myReadBuffer, 0, 1, SocketFlags.Peek);
                    return myReadBuffer[0];
                }
                else throw new TimeoutException();
            }
            catch (TimeoutException)
            {
                if (TO.Fatal) CloseSocket();
                throw;
            }
            catch
            {
                CloseSocket();
                throw;
            }
        }
        /// <summary>
        /// Write string
        /// </summary>
        /// <param name="s">Value</param>
        /// <returns>True if writen</returns>
        public bool Write(string s)
        {
            return s != null ? Write(Encoding.UTF8.GetBytes(s)) : false;
        }
        /// <summary>
        /// Write ByteArray
        /// </summary>
        /// <param name="ByteArray">Buffer</param>
        /// <returns>True if writen</returns>
        public bool Write(byte[] ByteArray)
        {
            if (_Socket != null && ByteArray != null)
            {
                try 
                {
                    _act = DateTime.Now.Ticks;
                    return _Socket.Send(ByteArray) == ByteArray.Length;
                }
                catch
                {
                    CloseSocket();
                }
            }
            return false;
        }

        #region Async methods
        /// <summary>
        /// Begin Receive
        /// </summary>
        /// <param name="buffer">Buffer</param>
        /// <param name="offset">Offset</param>
        /// <param name="size">Count</param>
        /// <param name="socketFlags">Socket flags</param>
        /// <param name="callback">AsyncCallback</param>
        /// <param name="state">State object</param>
        /// <returns></returns>
        public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state)
        {
            return _Socket.BeginReceive(buffer, offset, size, socketFlags, callback, state);
        }

        /// <summary>
        /// EndReceive
        /// </summary>
        /// <param name="asyncResylt">AsyncResult</param>
        /// <returns></returns>
        public int EndReceive(IAsyncResult asyncResylt)
        {
            _act = DateTime.Now.Ticks;
            return _Socket.EndReceive(asyncResylt);
        }
        #endregion

        #region IDisposable Members

        /// <summary>
        /// Dispose object
        /// </summary>
        public void Dispose()
        {
            try 
            {
                CloseSocket();
            }
            catch{}
        }

        #endregion
    }

    #endregion

    #region CRC16-CCITT
    //TODO: add summary comments on CRC16

    /// <summary>
    /// 
    /// </summary>
    public static class CRC16CCITT
    {
        private static UInt16[] pCRC16CCITTTable = new UInt16[] { 
           0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 
            0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 
            0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 
            0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 
            0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 
            0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 
            0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 
            0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 
            0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 
            0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 
            0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405, 
            0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 
            0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3, 
            0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92, 
            0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1, 
            0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0
       };


        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static ushort Calc(byte[] buffer, int offset, int length)
        {
            UInt16 val = 0xFFFF;
            int il = offset + length;
            for (long i = offset; i < il; i++)
            {
                val = (UInt16)(pCRC16CCITTTable[(val >> 8) ^ buffer[i]] ^ (val << 8));
            }
            return val;
        }
    }

    #endregion

}
